# Maják Maják je CMS pro Pirátské weby. Postavený je na [Wagtail](https://wagtail.io/). [](https://github.com/psf/black) [](https://wagtail.io) [](https://www.djangoproject.com) [](LICENSE) ## Pod pokličkou [Wagtail](https://wagtail.io) a [Django](https://www.djangoproject.com) jsou poměrně vyspělé frameworky. Vždy mysli na to, že problém co řešíš, už pravděpodobně řešil někdo před tebou. A obvykle existuje elegantní řešení. Pár užitečných odkazů: * [docs Wagtail](https://docs.wagtail.io/) * [docs Django](https://docs.djangoproject.com/) A za zmínku stojí [Awesome Wagtail](https://github.com/springload/awesome-wagtail) jako přehled pluginů a rozšíření pro Wagtail. Rozšíření která používáme: * [wagtail-metadata](https://github.com/neon-jungle/wagtail-metadata) - upravený template `shared/templates/wagtailmetadata/parts/tags.html` ### Struktura projektu . ├── donate = app na web dary.pirati.cz ├── elections2021 = app na količní web k volbám 2021 ├── senate = app na web senat.pirati.cz ├── senat_campaign = app na weby kandidátů na senátory ├── districts = app na web kraje.pirati.cz ├── district = app na weby oblastních sdružení ├── uniweb = app na univerzalni webove stranky ... ├── majak = Django projekt s konfigurací Majáku ├── shared = app se sdílenými static soubory a templaty pro weby ├── calendar_utils = app s modelem a utilitami na iCal kalendáře ├── tuning = app na tuning administračního rozhraní Majáku └── users = app s custom user modelem a SSO, apod. Poznámky k uspořádání: * nesdílíme a nemícháme modely zděděné z `Page` mezi jednotlivými appkami a weby, abychom předešli rozbití cizího webu * části definice tříd stránek sdílíme jako mixin pattern (`shared.models.ArticleMixin`, `share.models.SubpageMixin`, apod.) * sdílet se mohou obyčejné Django modely z `models.Model`, ale umístěné v nějaké appce/knihovně určené pro sdílení (`shared`, `calendar_utils`, apod.), nikoliv mezi weby (mezi appkami na weby by nikdy neměly vzniknout závislosti) ### Konfigurace webu Konfigurace konkrétního webu (odkazy do patičky, Matomo ID, apod.) se definuje v kořenové `xxxHomePage` webu. Je to pro uživatele snažší na správu než [Site Settings](https://docs.wagtail.io/en/stable/reference/contrib/settings.html) Wagtailu pro konfigurace webů. A pro vývojáře je to skoro jedno. Z různých podstránek webu se k té konfiguraci dostaneme přes property `root_page` kterou přidává `shared.models.SubpageMixin`. V `xxxHomePage` webu je třeba definovat `root_page` tak, aby vedla na `self`. ### Styleguide Některé weby využívají [Pirátskou UI styleguide](https://gitlab.pirati.cz/to/weby/ui-styleguide) pro vzhled. Idea je, že se budou sdílet nejen statické assety (CSS, JS, ...), ale i univerzální kusy template, které se dají includovat v různých webech (patička webu, karta článku do přehledu, atp.). Sdílené části využívané na více webech se umísťují do: shared/static/styleguideXX/ = statické assety (CSS, JS, obrázky, ...) shared/templates/styleguideXX/ = snippety pro include v templatech `XX` v názvu adresáře je číslo major a minor verze šablony. Např. `styleguide18` obsahuje věci ze šablony verze `1.8.x`. Důvodem je snažší migrace na novější verze šablony, které mohou obsahovat nekompatibilní změny. Každý web tak může (a nemusí) migrovat nezávisle dle potřeby. Různé verze šablony jsou k vidění na [styleguide.pirati.cz](https://styleguide.pirati.cz/) ### Kalendáře Pro práci s kalendáři v iCal formátu je připravena appka `calendar_utils`. Poskytuje `CalendarMixin` do modelu, který přidá fieldy `calendar_url` pro editaci a `calendar` pro vazbu na model `Calendar` (který se plní a automaticky spravuje na pozadí). Typicky se použije ve Wagtail settings pro web, kde stačí `calendar_url` zpřístupnit pro editaci. Kalendář se stáhne při uložení modelu obsahujícího `CalendarMixin`. Appka přidává management command `update_callendars`, který stahuje a updatuje kalendáře. Je třeba ho pravidelně volat na pozadí (přes CRON). ### Celery (Import z Jekyllu) Import z Jekyll GitHub repozitářů pirátských webů je řešen asynchroně přes Celery. Celery využívá Redis, který může běžet např. lokálně, typicky 6379: V envu pak je pak potřeba mít nastavený Celery broker: ``` CELERY_BROKER_URL=redis://localhost:6379/6 CELERY_RESULT_BACKEND=redis://localhost:6379/6 ``` Aby se celery tasky vykonávaly, je potřeba pustit celery v terminálu: ``` celery -A majak worker ``` Pokud není zadán `CELERY_BROKER_URL`, tak se automaticky nastaví `CELERY_TASK_ALWAYS_EAGER = True`, čímž se tasky vykonají synchronně přímo v procesu webserveru. ### Stránka 404 Pokud je třeba vlastní 404 pro web, stačí do kořenové `xxxHomePage` webu definovat metodu `get_404_response` vracející Django HTTP Reponse objekt. def get_404_response(self, request): return HttpResponse("Stránka nenalezena", status=404) ## Deployment ### Konfigurace Je třeba nastavit environment proměnné: | proměnná | default | popis | | --- | --- | --- | | `DATABASE_URL` | | DSN k databázi (např. `postgres://user:pass@localhost:5432/majak`) | | `OIDC_RP_REALM_URL` | | OpenID server realm URL (např. `http://localhost:8080/auth/realms/master/`) | | `OIDC_RP_CLIENT_ID` | | OpenID Client ID | | `OIDC_RP_CLIENT_SECRET` | | OpenID Client Secret | | `BASE_URL` | https://majak.pirati.cz | základní URL pro notifikační emaily apod. | V produkci musí být navíc nastaveno: | proměnná | default | popis | | --- | --- | --- | | `DJANGO_SECRET_KEY` | | tajný šifrovací klíč | | `DJANGO_ALLOWED_HOSTS` | | allowed hosts (více hodnot odděleno čárkami) | | `CELERY_BROKER_URL` | | URL pro Celery Broker | | `CELERY_RESULT_BACKEND` | | URL pro Celery Result Backend | | `EMAIL_HOST` | SMTP pro odesílání přihlášek do kariér | | `EMAIL_HOST_USER` | --||-- Username | | `EMAIL_HOST_PASSWORD` | --||-- Heslo | | `EMAIL_PORT` | --||-- Port | | `CLAMD_TCP_ADDR` | ClamAV host (pro skenování virů v nahraných souborech) | | `CLAMD_TCP_SOCKET` | ClamAV socket | Různé: | proměnná | default | popis | | --- | --- | --- | | `MAJAK_ENV` | prod | `prod`/`test`/`dev` prostředí kde Maják běží | | `SENTRY_DSN` | | pokud je zadáno, pády se reportují do Sentry | | `SEARCH_CONFIG` | english | nastavení jazyka fulltextového vyhledávání, viz níže | | `DEBUG_TOOLBAR` | False | zobrazit Django Debug Toolbar (pro vývoj) | | `MAILTRAIN_API_URL` | | URL k API Mailtrain | | `MAILTRAIN_API_TOKEN` | | token k API Mailtrain | Settings pro appky na weby: | proměnná | default | popis | | --- | --- | --- | | `DONATE_PORTAL_REDIRECT_URL` | "" | URL pro přesměrování z darovacího formuláře | | `DONATE_PORTAL_REDIRECT_SOURCE` | dary.pirati.cz | identifikátor zdroje pro přesměrování na darovací portál | | `DONATE_PORTAL_API_URL` | "" | URL s API darovacího portálu | ### Management commands Přes CRON je třeba na pozadí spouštět Django `manage.py` commandy: * `clearsessions` - maže expirované sessions (denně až týdně) * `publish_scheduled_pages` - publikuje naplánované stránky (každou hodinu) * `update_callendars` - stáhne a aktualizuje kalendáře (několikrát denně) * `update_main_timeline_articles` - aktualizuje články na `pirati.cz` z `https://piratipracuji.cz/api/` * `update_redmine_issues` - aktualizuje programované body MS a KS stránek napojených na Redmine (několikrát denně) ### Fulltextové vyhledávání v češtině Pro fulltextové vyhledávání je třeba do PostgreSQL přidat [slovníky](https://github.com/f00b4r/postgresql-czech) do adresáře `/usr/local/share/postgresql/tsearch_data/`. V databázi Majáku je třeba nakonfigurovat český fulltext: ```sql CREATE TEXT SEARCH DICTIONARY cspell (template=ispell, dictfile=czech, afffile=czech, stopwords=czech); CREATE TEXT SEARCH CONFIGURATION czech (copy=english); ALTER TEXT SEARCH CONFIGURATION czech ALTER MAPPING FOR word, asciiword WITH cspell, simple; CREATE extension IF NOT EXISTS unaccent; ``` Otestovat funkčnost lze dotazem: ```sql SELECT * FROM ts_debug('czech', 'Příliš žluťoučký kůň se napil žluté vody'); ``` Dále nastavit environment proměnnou `SEARCH_CONFIG=czech`. A nakonec jednorázově spustit naplnění indexu pro vyhledávání `python manage.py update_index`. Aktualizace indexu poté probíhají automaticky při uložení stránek. ### Přidání nového webu Doména či subdoména se musí nakonfigurovat v: * environment proměnné `DJANGO_ALLOWED_HOSTS` * proxy před Majákem ## Vývoj Pro vývoj je definován pomocný `Makefile` pro časté akce. Pro nápovědu zavolej: $ make help ### Lokální instalace a spuštění #### Vytvoření virtualenv pro instalaci závislostí Vytvoř virtualenv: $ make venv Vytvoří virtualenv ve složce `.venv`. Předpokládá že výchozí `python` v terminálu je Python 3. Pokud tomu tak není, použijte třeba [Pyenv](https://github.com/pyenv/pyenv) pro instalaci více verzí Pythonu bez rizika rozbití systému. #### Aktivace virtualenvu Před prací na projektu je třeba aktivovat virtualenv. To bohužel nejde dělat pomocí nástroje `make`. Je třeba zavolat příkaz: $ source .venv/bin/activate Můžete asi na to vytvořit alias pro shell. Do `~/.bash_profile` nebo `~/.zshrc` nebo jiného konfiguračního souboru dle vašeho shellu přidejte: alias senv='source .venv/bin/activate' A pak můžete virtualenv aktivovat pouze jednoduchým voláním: $ senv Pro sofistikovanější řešení, které vám aktivuje virtualenv při změně adresáře na adresář s projektem, slouží nástroj [direnv](https://direnv.net/). Deaktivace virtualenvu se dělá příkazem: $ deactivate #### Instalace závislostí V aktivovaném virtualenvu spusťte: $ make install To nainstaluje Pythonní závislosti pro vývoj projektu na lokále. #### Nastavení environment proměnných Environment proměnné (viz konfigurace výše) se načítají ze souboru `.env`, který může vypadat takto: DATABASE_URL=postgres://db:db@localhost:5432/majak OIDC_RP_REALM_URL=http://localhost:8080/auth/realms/master/ OIDC_RP_CLIENT_ID=majak OIDC_RP_CLIENT_SECRET=abcd Pro lokální vývoj obsahují settings tyto výchozí hodnoty: DEBUG = True ALLOWED_HOSTS = ["*"] MAJAK_ENV = "dev" #### Autentifikace a autorizace Jako OIDC server můžete použít lokálně spuštěný Keycloak z [Dev Tools](https://gitlab.pirati.cz/to/dev-tools). Prvním přihlášením přes OIDC se vytvoří uživatel, ale bez oprávnění. Je třeba mu přes Django shell (viz níže) nastavit oprávnění pro přístup do administrace: ```python from users.models import User u = User.objects.get() u.is_superuser = True u.is_staff = True u.save() ``` ### Management projektu #### Migrace databáze Aplikuj migrace databáze: $ make migrate Při změně modelů vygeneruj migrace pomocí: $ make migrations #### Spuštění development serveru Django development server na portu `8006` se spustí příkazem: $ make run Poté můžete otevřít web na adrese [http://localhost:8006](http://localhost:8006) ##### Debug Toolbar Pro spuštění development serveru s Django Debug Toolbar nastavte environment proměnnou `DEBUG_TOOLBAR`. Např.: $ DEBUG_TOOLBAR=1 make run #### Django shell Django shell používající `shell_plus` z Django extensions spustíte: $ make shell ### Lokální weby Pro vývoj různých webů si nastavte v `/etc/hosts` (na Linux, Mac OS, apod.) pojmenované aliasy k lokální IP 127.0.0.1 jako např.: 127.0.0.1 majak.loc dalsiweb.loc K Majáku spuštěnému v development serveru na portu `8006` se pak dostanete na adrese [http://majak.loc:8006/admin/](http://majak.loc:8006/admin/). A pokud nějakému webu v nastavení v Majáku dáte adresu třeba `dalsiweb.loc` a port `8006`, dostanete se pak na něj přes adresu [http://dalsiweb.loc:8006](http://dalsiweb.loc:8006). ### Testy Používá se testovací framework [pytest](https://pytest.org). Spuštění testů: $ pytest Případně přes `make`, ale bez možnosti parametrizovat spuštění testů: $ make test Coverage report: $ make coverage ### Code quality K formátování kódu se používá [black](https://github.com/psf/black). Doporučujeme ho nainstalovat do vašeho editoru kódu, aby soubory přeformátoval po uložení. Přeformátování kódu nástrojem `black` je součástí `pre-commit` hooks (viz níže). Součástí `pre-commit` hooků je také automatické seřazení importů v Pythonních souborech nástrojem [isort](https://github.com/timothycrosley/isort/). ### Pre-commit hooky Použivá se [pre-commit](https://pre-commit.com/) framework pro management git pre-commit hooks. Máte-li pre-commit framework [nainstalovaný](https://pre-commit.com/#installation) spusttě příkaz: $ make install-hooks Ten naisntaluje hooky pro projekt. A poté při každém commitu dojde k požadovaným akcím na změněných souborech. Ručně se dají hooky na všechny soubory spustit příkazem: $ make hooks ## Upgrade závislostí K upgrade se používají [pip-tools](https://github.com/jazzband/pip-tools) (`pip install pip-tools`): $ cd requirements/ $ pip-compile -U base.in $ pip-compile -U production.in $ pip-compile -U dev.in Tím se vygenerují `base.txt`, `production.txt` a `dev.txt`.