foodsharing

Overview

This is the developer documentation for the code that powers foodsharing.de, foodsharing.at, and foodsharingschweiz.ch.

Useful links

Die foodsharing-IT sucht Helfer*innen 💻🔧

For the english version please click here.

Lieber Interessent (w/m/d),

es freut uns, dass du dich für dieses Thema interessierst. Wir von foodsharing sind eine Initiative, mit dem Ziel sich gegen Lebensmittelverschwendung einzusetzen: https://foodsharing.de/ueber-uns Unsere Homepage ist das zentrale Werkzeug unserer Initiative. Die gesamte Vernetzung untereinander, die gemeinsame Arbeit an verschiedensten Themen und zu guter Letzt die Organisation all unserer Lebensmittelrettungen sind dank dieser Plattform möglich.

Was viele aber nicht wissen: Wir haben mehr als 300.000 registrierte Konten und verschicken hunderttausende Mails pro Monat. Ein riesen Apparat. Etwa 66.000 Foodsaver organisieren sich über die Homepage, viele hunderte Botschafterinnen und Botschafter verwalten unterschiedlichste Regionen im gesamten deutschsprachigen Raum. Dem gegenüber stehen im Schnitt etwa nur 5 Programmierer*Innen. Daher wollen wir mit diesem aufgefrischten Post jetzt im Juni 2019 einen neuen Aufruf starten.

Du hast jetzt bereits Lust bekommen?

Melde dich gerne bei Slack https://slackin.yunity.org im Kanal/Gruppenchat #foodsharing-dev oder sende uns eine Mail an it@foodsharing.network damit wir mit dir gemeinsam mögliche Aufgaben und Betätigungsfelder rund um die unten aufgelisteten Themen für dich finden können. Wenn diese beiden Kontaktwege dich abhalten, kannst du auch gerne Jonathan über die Webseite eine Nachricht schreiben. Wir sind ein tolles Team und wir haben bisher für jedeN die/der helfen wollte eine Aufgabe gefunden.

Wo sind all die Freiwilligen mit IT-Kenntnissen?

Wir haben so viele hochaktive und überzeugte Mitstreiter in unserem schönen Projekt. Doch nur so wenige bringen sich bisher in die IT ein. Wo sind denn all die Hobby-Programmierer? Wir müssten bei einer so jungen und frischen Einrichtung doch Dutzende haben.

Was benötigen wir?

Programmierer*innen

  • Android Programmierer*innen (nativ)
  • iOS-Programmierer*innen (nativ)
  • Web-Entwickler*innen

Qualitätssicherung

  • Beta-Tester*innen
  • Issue-Betreuer*innen
  • Unit-Test-Entwickler*innen

Design

  • Grafikdesigner*innen
  • Android-Frontend-Designer*innen (Verbesserung des App-Designs)

Support

  • Webseiten-Supporter*innen (deutschsprachig)
  • IT-Supporter*innen
  • Technische Redakteure
  • Recruiter*innen

Wie finden wir mehr UnterstützerInnen?

Frag doch einmal in Deinem Bezirk herum. Innerhalb von foodsharing, aber gerne auch außerhalb. Es gibt bestimmt Leute, die sich gerne einbringen möchten, aber keine Lust oder Zeit haben für unser übliches Lebensmittelrettungsprogramm. Du kannst auch gerne Freiwilligenstellen ausschreiben oder auf Infoveranstaltungen nach Interessenten suchen. Du wirst überrascht sein, wie viele Leute heutzutage programmieren können. Hilf uns, diese Personen zu motivieren und zu überzeugen.

Was gibt es zu tun?

Abhängig davon, welche Motivation Du mitbringst, findet sich gewiss eine Aufgabe für dich. Mögliche Aufgaben sind unter anderem:

  • Die rund 250 derzeit gemeldeten Fehler oder Feature Wünsche angehen
  • App für jeweils Android und iOS
  • Die vielen Botschafter*innen aber auch Mitglieder bei ihren IT-Problemen zu unterstützen
  • Die Plattform ins Englische transferieren
  • Akkumulation und Moderation der Issues und Feature-Wünsche
  • Auf Freiwilligenplattformen für uns neue Programmierer*innen werben
  • ...

Wie kannst Du Dich einbringen?

Wie schon gesagt, mach ordentlich Werbung für unsere IT und sende den Link zu diesen Informationen hier gerne weiter. Besonders schön wäre es aber natürlich, Dich dabei zu haben:

  1. Schau Dich doch mal auf unserem Blog um, oder in unserem Contribution Guide.
  2. Sieh Dir die lange Liste unserer To-dos an. Einige davon sind speziell für Anfänger markiert.
  3. Besonders freuen wir uns, wenn Du in unserem Slack Chat vorbei schaust und wir im Kanal #foodsharing-dev mit Dir gemeinsam mögliche Aufgaben und Betätigungsfelder in der IT für dich finden.
  4. Wenn Du unsicher bist oder die englischen Programmiererseiten nicht verstehst, dann darfst Du uns gerne eine Mail schreiben oder auf Peter zukommen. Wir versuchen weiterzuhelfen.

Schickte uns auch gerne eine Mail an it@foodsharing.network wo wir weiter darüber sprechen können wie Du Dich einbringen kannst.

Gibt es nicht noch 'ne nerdige Auflistung, um was es IT-technisch geht?

Aber natürlich:

Webseiten Front- und Backend

  • PHP (Symfony 5)
  • JavaScript (Webpack, Vue.js)
  • HTML (Twig)
  • CSS (Bootstrap)
  • MySQL
  • RESTful APIs
  • Git
  • Docker Compose development environment
  • Codeception für Unit-, API-, and Acceptance testing (mit Selenium)
  • GitLab CI für Tests und automatisches deployment (mit php deployer)

Generelles zu den Smartphone Apps

  • Das Backend wächst und wird in gemeinsamer Arbeit auf die Bedürfnisse der App Entwickler*innen angepasst
  • Beim Setup wirst Du durch die Infos in der Gitlab Readme und durch uns unterstützt
  • Die Roadmap bestimmst Du maßgeblich selbst mit
  • Screen / Workflow Designer für ein grundlegendes Design sind hier auch sehr willkommen
  • Wertvolle Unterstützung durch den CI Prozess

Google Android App:

  • Kotlin als Sprache soweit wie möglich
  • REST/APIs (retrofit)
  • Dependency injection (dagger)
  • Reactive programming (reactivex)

Apple iOS App:

  • Carthage als Packet Manager
  • Die grundlegende Architektur der App baut auf ReSwift auf
  • Views werden im Code, mit AutoLayout und Constraints erzeugt
  • Als UI Architektur wird RxSwift verwendet
  • Coordinator Pattern als Screenflow

Hackweeks

Um noch enger Hand in Hand zu arbeiten, treffen wir uns regelmäßig zu Hackweeks. Hier sind alle Teilnehmer*innen willkommen, die das Ziel haben, an der Entwicklung mitzuwirken.

Hier ein paar Artikel über die letzte Hackweek:

  • https://yunity.org/en/heartbeat/2019-02-24
  • https://foodsharing.de/?page=blog&sub=read&id=236

Ankündigungen für Hackweeks findest Du entweder im #foodsharing-hackweek Slack Channel oder hier:

  • https://devblog.foodsharing.de/
  • https://yunity.org/heartbeat

Danke!

Danke Dir, dass Du so lange gelesen hast. Mit einer fitten IT hätten wir so viel mehr Potential und es wäre natürlich generell toll, die vielen Aufgaben auf viele Schultern zu verteilen. Mit einem noch cooleren IT-Team können wir in guter Stimmung eigentlich alles umsetzen, was wir uns wünschen. Und man lernt nebenbei 'nen ganzen Haufen spannenden Kram. Wir freuen uns schon sehr darauf, Dich und Deine Bekannten bald im Team willkommen zu heißen.

📣📣 Vielleicht ist es leider wirklich nichts für Dich, schicke aber bitte Du trotzdem jemanden den Link zu dieser Seite. Damit HILFST DU uns und somit der ganzen Community WIRKLICH SEHR. ❤️ ❤️ https://devdocs.foodsharing.network/it-tasks.html

Herzliche Grüße von Deinem IT-Team! it@foodsharing.network

(Letzte Änderung: 23.12.2019)

The foodsharing-IT is looking for helpers 💻🔧

Eine Deutsche Version ist hier zu finden.

Dear potentially interested people (f/m/d),

we are pleased that you are interested in this topic. We at foodsharing are an initiative to fight food waste: https://foodsharing.de/ueber-uns (link to page in German) Our homepage is the central tool of our initiative. This platform enables the entire networking among each other, the common work on different topics and last but not least the organisation of all our food rescues.

What many might not know: Currently we more than 300.000 registered accounts and send out hundreds of thousands of emails every month. A giant apparatus. Around 66.000 food savers are organised via the homepage, and many hundreds of ambassadors (Botschafter) administer a wide variety of regions throughout the German-speaking world. On the other hand, there are on average only 5 programmers.. That's why we want to launch a new call with this refreshed post in June 2019.

Have we gotten you interested?

Feel free to contact us on Slack (https://slackin.yunity.org) in the channel/group chat #foodsharing-dev or send us an email to it@foodsharing.network so that we can find together some possible tasks and fields of activity around the topics listed below for you. Should these two ways of contact keep you away, you can also write a message to Jonathan via the website. We are a great team and so far we have found a task for everyone who wanted to help.

Where are all the volunteers with IT skills?

We have so many highly active and committed volunteers in our marvellous project. But only a few of them are involved in IT so far. Where are all the hobby programmers? One might expect to find dozens at such a young and fresh institution.

What do we need?

Programmers

  • Android Programmers (native)
  • iOS programmers (native)
  • web developers

quality assurance

  • Beta testers
  • issue managers
  • Unit Test Developers

design

  • graphic designers
  • Android front-end designers (improvement of app design)

support

  • Website supporters (German-speaking)
  • IT supporters
  • Technical Editors
  • recruiters

How do we find more supporters?

Ask around in your district: Inside foodsharing, but also outside. There are certainly people who would like to get involved, but have no desire or time for our usual food rescue program. You can also advertise volunteer positions or search for interested people at information events. You'll be surprised how many people can program these days. Help us motivate and convince these people.

What is there to do?

Depending on the motivation you bring along, there will certainly be a task for you. Possible tasks are among others:

  • Approximately 250 currently reported bugs or feature requests are to be addressed
  • App for Android and iOS respectively
  • To support the many ambassadors as well as members with their IT problems
  • Translating the platform into English
  • Accumulation and moderation of issues and feature requests
  • Advertise new programmers for us on volunteer platforms
  • ...

How can you get involved?

As already said, you can do some good advertising for our IT and send the link to this information here. But of course it would be especially nice to have you here:

  1. Have a look around on our blog or in our Contribution Guide.
  2. Have a look at the long list of our To-dos. Some of them are specially marked for beginners.
  3. We are especially happy if you drop by in our Slack Chat and we find together with you possible tasks and fields of activity in IT for you in the channel #foodsharing-dev.
  4. If you are unsure or don't understand the English programmer pages, you are welcome to send us an email or approach Peter. We'll try to help you.

We would also be happy to receive an email to it@foodsharing.network and to talk about how you can get involved.

Isn't there some kind of nerdy listing in tech speak that states, what is needed?

But of course:

Website Front- and Backend

  • PHP (Symfony 5)
  • JavaScript (Webpack, Vue.js)
  • HTML (Twig)
  • CSS (Bootstrap)
  • MariaDB
  • RESTful APIs
  • Git
  • Docker Compose development environment
  • Codeception for Unit-, API-, and Acceptance testing (with Selenium)
  • GitLab CI for tests and automatic deployment (with php deployer)

General information for the smartphone app

  • The backend grows and is cooperatively being adapted to the needs of the app developers
  • During the setup you will be supported by the information in the Gitlab Readme and by us
  • The roadmap is determined to a large extent by the roadmap itself
  • Screen / workflow designers for a basic design are also very welcome here
  • Valuable support through the CI process

Google Android App:

  • Kotlin as language as far as possible
  • REST/APIs (retrofit)
  • dependency injection (dagger)
  • Reactive programming (reactivex)

Apple iOS App:

  • Carthage as Packet Manager
  • The basic architecture of the app is based on ReSwift
  • Views are created in code, with AutoLayout and constraints
  • RxSwift is used as UI architecture
  • Coordinator Pattern as Screenflow

Hackweeks

In order to work even more closely together, we meet regularly for Hackweeks. Here all participants are welcome to participate in the development.

Here are some articles about the last Hackweek:

  • https://yunity.org/en/heartbeat/2019-02-24
  • https://foodsharing.de/?page=blog&sub=read&id=236

Announcements for Hackweeks can be found either in the #foodsharing-hackweek Slack Channel or here:

  • http://devblog.foodsharing.de/
  • https://yunity.org/heartbeat

Thank you!

Thank you for reading so long. With a fit IT we have so much more potential and of course it would be great to distribute the many tasks on many shoulders. With an even cooler IT team, we can do whatever we want in a good mood. And you learn a whole bunch of exciting stuff on the side. We are really looking forward to welcoming you and your friends to the team soon.

📣📣 Maybe it's really not for you, but please send someone the link to this page. So YOU CAN HELP us and thus the whole community A LOT! ❤️ ❤️
https://devdocs.foodsharing.network/it-tasks.html

Best regards from your IT team! it@foodsharing.network

(Last modification: 23.12.2019)

Getting the code

We use the version control system git. The code is hosted on gitlab. We are Open Source so you can just clone the repo and start exploring.

For Git, we recommend to use SSH (and the following documentation is supposing that). See e.g. this documentation if you need to configure GitLab for this.

For first use of git search for tutorials, there are a lot good ones out there.

For exploring the project, check Running the code and Setting things up afterwards. For contributing like writing issues and creating merge requests, check out the Contributing section, and join our mentioned Slack channel (slackin.yunity.org , channel #foodsharing-dev).

An den Code kommen (Übersetzung)

Wir verwenden das Versionskontrollsystem git. Der Code wird auf gitlab gehostet. Wir sind Open Source, so dass Du das Repository ("Repo") einfach klonen und mit der Erkundung beginnen kannst.

Für Git empfehlen wir die Verwendung von SSH (das Secure Shell Netzwerkprotokoll, die folgende Dokumentation hat das zur Voraussetzung). Wenn du GitLab dafür konfigurieren musst, hilft vermutlich z.B. diese Dokumentation.

Für die erste Benutzung von Git such online nach einem Tutorial. Es gibt eine Menge gute Anleitungen.

Um das Projekt zu erkunden, mach anschließend weiter bei Running the code und Setting things set-up. Für Beiträge wie das Schreiben von issues und das Erstellen von Merge-Requests schau in den Abschnitt Einführung in Git und unser Arbeiten an, und komm in unseren Slack-Kanal (slackin.yunity.org , Kanal #foodsharing-dev).

Running the code

(Unten gibt es eine deutsche Übersetzung)

We use Docker and Docker Compose.

You can use the Docker Compose setup if you are using one of the following:

  • Linux (64bit)
  • OSX Yosemite 10.10.3 or higher
  • Windows 10 Pro or higher

If you are not using one of those, then try the Vagrant + Docker Compose setup.

Linux

Install Docker CE.

And make sure you have Docker Compose (at least version 1.6.0) installed too (often comes with Docker).

Fedora 31

Switched to using cgroupsV2 by default, which is not yet supported from docker.

To disable v2 cgroups, run:

sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0"

And restart your machine.

Fedora 32 or Debian 10

Some distributions use as default firewall backend nftables. Docker currently only supports iptables. The containers cannot establish network connections with each other.

Workaround: You have to set the entry FirewallBackend = iptables in /etc/firewalld/firewalld.conf.

After a restart the services firewalld and docker it should work.

If you cannot connect to Docker with your local user, then you may want to add yourself to the Docker group:

Fedora 32

Download docker from: https://download.docker.com/linux/fedora/31/x86_64/stable/Packages/

sudo dnf -y install /path/to/package.rpm
sudo systemctl start docker

Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown

sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

General on Linux

sudo usermod -aG docker $USER

Then either log in again to reload the groups or run (for each shell...) su - $USER

Should now be able to connect without errors

docker info

Then:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
./scripts/start

OSX Yosemite 10.10.3 or higher

Install Docker for Mac (direct link).

Then:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
./scripts/start

Windows

New approach on WSL2

  • Install Ubuntu 20.xx via Windows AppStore (WSL2) (you need to start it once for step 3)
  • Install windowsTerminal via Appstore
  • Open a new tab in WinTerminal with Ubuntu
  • Change to your target directory
  • Execute git download (you may need to add an ssh key from this environment)
`git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
  • Add the following to ~/.bashrc with editor (e.g. nano)
export DOCKER_HOST=tcp://localhost:2375
export DOCKER_BUILDKIT=1
  • Check in docker settings - Resources - WSL Integration your environment is active
cd /foodsharing
sudo./scripts/start
sudo./scripts/seed

On WSL1

If you are using Windows 10 Pro or higher go with this link:

Install Docker for Windows (direct link) and Git for Windows.

If you are using Windows 10 Home, make sure you fulfill all system requirements and then install both Docker Desktop on Windows Home and Git for Windows.

It is important to grant docker access to C: (in the graphical docker interface: settings -> resources -> filesharing -> mark C, apply and restart)

You can test your docker in the command shell (e.g. cmd or powershell) with the command docker --version. If it shows something, you're good to go.

Restart your Windows now.

There is a graphical user interface to administrate the repo, which is recommended for Git beginners.

But you can use the Git Bash shell just like in Linux to clone it:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing

After this command, your files will be found in the folder %UserProfile%\foodsharing

To start the containers, use the Git Bash shell:

cd foodsharing
./scripts/start

The first time you run the start script, which takes a lot of time, you probably have to give the windows firewall the OK to let Docker work.

Known Issues on Windows

  • general

If something is wrong, please check in your task manager under "performance" if the virtualisation is activated and troubleshoot if necessary.

  • git trouble (on WSL1)

If git does not working well, please do:

cd foodsharing/bin
tr -d '\15' < console > console

Make sure not to commit the console file and maybe discuss further steps with the team.

  • [RuntimeException]

If you get a [RuntimeException], let ./scripts/start run again and again and maybe even again until it's done.

  • yarn lint

There is a known bug concerning yarn, see: https://github.com/yarnpkg/yarn/issues/7187 and https://github.com/yarnpkg/yarn/issues/7732 and https://github.com/yarnpkg/yarn/issues/7551

  • Changes in js, vue etc. aren't showing up

In order to have the webpack-dev-server recognize changes you have to add this watchOptions block to client/serve.config.js

[...]
module.exports = {
  [...]
  devServer: {
    watchOptions: {
      poll: true
    },
    [...]

Note: Please make sure not to commit this file afterwards with your changes.

Vagrant

If you cannot use any of the above methods, then this should work with every common operation system.

However, we are less familiar with this solution, so we may be less able to support you.

Install VirtualBox and Vagrant.

Then:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
vagrant up

Daily work

vagrant up starts the machine and foodsharing project.

vagrant halt stops the virtual machine.

vagrant ssh connects to the virtual machine.

Once connected to the virtual machine, go to /vagrant with cd /vagrant. This is where the foodsharing folder is mounted in the VM. From there on, you can run all scripts with ./scripts/NAME.

Note: ./scripts/start will always be executed, when you start the virtual machine with vagrant up.

There is a known bug when running VirtualBox + nginx that nginx serves files from a memory cache. If you encounter this problem, then it can probably be fixed by emptying the memory cache with sync; sudo sh -c "/bin/echo 3 > /proc/sys/vm/drop_caches" or even running this every second with watch -n 1 'sync; sudo sh -c "/bin/echo 3 > /proc/sys/vm/drop_caches"'.


Den Code zum laufen bringen

Wir benutzen Docker und Docker Compose.

Du kannst das Docker Compose setup benutzen, falls du mit

  • Linux (64bit)
  • OSX Yosemite 10.10.3 oder darüber - oder aber
  • Windows 10 Pro oder darüber arbeitest.

Falls du keines von denen nutzt, probiere es mit Vagrant + dem Docker Compose Setup.

Linux

Installiere Docker CE.

Stelle sicher, dass du Docker Compose (mindestens version 1.6.0) auch installiert hast (gibt es oft zusammen mit Docker).

Fedora 31

Fedora benutzt mittlerweile cgroupsV2 als Standart, was noch nicht von Docker unterstützt wird. Um es abzustellen, lautet der Befehl:

sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0"

Danach neu starten.

Fedora 32 oder Debian 10

Manchmal wird als Standard firewall backend nftables genutzt. Docker unterstützt derzeit nur iptables. Die Docker-Container können dann keine Netzwerkverbindung miteinander herstellen.

Hilfestellung: Setze den Eintrag FirewallBackend = iptables in /etc/firewalld/firewalld.conf.

Nach einem Neustart sollten firewalld und Docker arbeiten.

Wenn du dich als lokaler Nutzer nicht mit Docker verbinden kannst, kann es helfen, dich selbst zur Docker Gruppe hinzuzufügen:

Fedora 32

Lade docker hier herunter: https://download.docker.com/linux/fedora/31/x86_64/stable/Packages/

sudo dnf -y install /path/to/package.rpm
sudo systemctl start docker

Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown

sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

General on Linux

sudo usermod -aG docker $USER

Dann logge dich entweder noch einmal ein oder lade die Gruppen neu oder führe (für jedes Fenster) aus: su - $USER

Jetzt solltest du dich ohne Fehler verbinden können.

docker info

Dann:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
./scripts/start

OSX Yosemite 10.10.3 oder darüber

Installiere Docker for Mac (direct link).

Dann:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
./scripts/start

Windows

Neuer Zugang bei WSL2

  • Installiere Ubuntu 20.xx via Windows AppStore (WSL2) (das musst du für Schritt drei einmal starten)
  • Installiere windowsTerminal via Appstore
  • Öffne einen neuen Tab im WinTerminal mit Ubuntu
  • Wechsle dein Zielverzeichnis
  • Führe aus git download aus (es kann sein, dass du einen SSH-Schlüssel für diese Umgebung hinzufügen musst)
`git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
  • Füge das Folgende ~/.bashrc mit einem Editor hinzu (z.B. nano)
export DOCKER_HOST=tcp://localhost:2375
export DOCKER_BUILDKIT=1
  • Prüfe in den Docker Einstellungen - Resources - WSL Integration, dass deine Umgebung aktiv ist.
cd /foodsharing
sudo./scripts/start
sudo./scripts/seed

Bei WSL1

Wenn du Windows 10 pro oder darüber nutzt, klick auf diesen Link:

Installiere Docker for Windows (Direktlink) und Git for Windows.

Wenn Du Windows 10 Home verwendest, stelle sicher, dass Du alle Systemanforderungen erfüllst. Dann installiere sowohl Docker Desktop on Windows Home und Git for Windows.

Es ist wichtig, Docker-Zugriff auf C: zu gewähren (in der grafischen Docker-Oberfläche: Einstellungen -> Ressourcen -> Filesharing -> C markieren, anwenden und neu starten).

Du kannst deinen in der Kommando-Shell (z.B. cmd oder powershell) mit dem Befehl docker --version testen. Wenn es etwas anzeigt, kannst du loslegen.

Starte jetzt dein Windows neu.

Es gibt eine grafische Benutzeroberfläche zur Verwaltung des Repos, die für Git-Anfänger empfohlen wird. Aber Du kannst die Git Bash-Shell genau wie unter Linux benutzen, um sie zu klonen:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing

Nach diesem Befehl befinden sich Deine Dateien im Ordner ````%UserProfile%\foodsharing```

Um die Container zu starten, verwende die Git Bash-Shell:

cd foodsharing
./scripts/start

Wenn Du das Startskript zum ersten Mal ausführst, was sehr viel Zeit in Anspruch nimmt, musst du wahrscheinlich der Windows-Firewall das OK geben, damit Docker funktioniert.

Bekannte Windows-Fehler

  • allgemein

Wenn etwas nicht in Ordnung ist, überprüfe bitte in Deinem Task-Manager unter "Leistung", ob die Virtualisierung aktiviert ist und behebe gegebenenfalls Fehler.

  • git Fehler (bei WSL1)

Wenn git nicht gut arbeitet, mach bitte folgendes:

cd foodsharing/bin
tr -d '\15' < console > console

Stell sicher, dass du die `Konsole'-Datei nicht committest und besprich vielleicht weitere Schritte mit dem Team.

  • [RuntimeException]

Wenn du eine [RuntimeException], bekommst, lass ./scripts/start noch einmal und wieder und wieder laufen, bis alles fertig ist.

  • yarn lint

Es gibt einen bekannten Fehler bezüglich yarn, siehe: https://github.com/yarnpkg/yarn/issues/7187 and https://github.com/yarnpkg/yarn/issues/7732 und https://github.com/yarnpkg/yarn/issues/7551

  • Veränderungen in js, vue etc. erscheinen nicht

Damit der webpack-dev-server Änderungen erkennt, musst du diesen watchOptions-Block zu client/serve.config.js hinzufügen:

[...]
module.exports = {
  [...]
  devServer: {
    watchOptions: {
      poll: true
    },
    [...]

Hinweis: Bitte achte darauf, diese Datei nicht nachträglich mit Ihren Änderungen zu übertragen.

Vagrant

Wenn Du keine der oben genannten Methoden verwenden kannst, dann sollte dies mit jedem gängigen Betriebssystem funktionieren. Allerdings sind wir mit dieser Lösung weniger vertraut, so dass wir Dich damit möglicherweise weniger gut unterstützen können:

Installiere VirtualBox und Vagrant.

Dann:

git clone git@gitlab.com:foodsharing-dev/foodsharing.git foodsharing
cd foodsharing
vagrant up

Für's Tägliche Arbeiten

vagrant up startet die Maschine und das Foodsharing-Projekt.

vagrant halt stoppt die virtuelle Maschine.

vagrant ssh verbindet sich mit der virtuellen Maschine.

Sobald die Verbindung mit der virtuellen Maschine hergestellt ist, gehe mit cd /vagrant nach /vagrant. Dort wird der Foodsharing-Ordner in der VM gemountet. Von dort aus kannst Du alle Skripte mit ./scripts/NAME ausführen.

Notiz: ./Skripte/Start wird immer ausgeführt, wenn Du die virtuelle Maschine mit vagrant up startest.

Es gibt einen bekannten Fehler beim Ausführen von VirtualBox + nginx, dass nginx Dateien aus einem Speicher-Cache bedient. Wenn Du auf dieses Problem stößt, dann kann es wahrscheinlich behoben werden, indem Du den Speicher-Cache mit sync; sudo sh -c "/bin/echo 3 > /proc/sys/vm/drop_caches" or even running this every second with watch -n 1 'sync; sudo sh -c "/bin/echo 3 > /proc/sys/vm/drop_caches"' ausführst.

Setting things up

You must have completed the installation setup before doing this.

Now go and visit localhost:18080 in your browser. You should see a foodsharing instance running on your local machine :)

For generating a bit of initial data to play with, execute the seeding script:

./scripts/seed

It will give you some users that you can log in with:

EmailPasswordRole
user1@example.comuserFoodsharer
user2@example.comuserFoodsaver
storemanager1@example.comuserStore manager
storemanager2@example.comuserStore manager
userbot@example.comuserAmbassador
userbot2@example.comuserAmbassador
userbotreg2@example.comuserAmbassador
userorga@example.comuserOrgateam

Please refer to the User Roles and Permissions section for details on the different roles.
Tip: You can use private browser windows to log in with multiple users at the same time!

The script also generates more dummy users and dummy data to fill the page with life (a bit at least). Should you want to modify it, have a look at the file /src/Dev/SeedCommand.php.

Whenever you make changes to non-PHP frontend files (e.g. .vue, .js or .scss files), those are directly reflected in the running docker instance. Changes to PHP files will require a page reload in your browser.

To stop everything again, just run:

./scripts/stop

PHPMyAdmin is also included: localhost:18081. Log in with:

FieldValue
Serverdb
Usernameroot
Passwordroot

There you can directly look at and manipulate the data in the database which can be necessary or very useful for manual testing and troubleshooting.

MailDev is also included: localhost:18084. There you can read all e-mails that you write via the front end.

php Code style

We use php-cs-fixer to format the code style. The aim is to make it use the same style as phpstorm does by default. The fixer is based on the @Symfony ruleset, with a few changes.

To format all files, you can run:

vendor/bin/php-cs-fixer fix --show-progress=estimating --verbose

For convenience, you can and should add the code style fix as a pre-commit hook. So you will never commit/push any PHP code that does not follow the code style rules.

There are two possibilities:

Using local PHP

When PHP >= 7.0 is installed locally and the vendor folder is in place (by having used the automated tests or the dev environment), you can use your computers PHP to check/fix the codestyle, as this is the fastest option:

./scripts/fix-codestyle-local

Adding this to .git/hooks/pre-commit could look like that:

#!/bin/sh
HASH_BEFORE=$(git diff | sha1sum)
./scripts/fix-codestyle-local
# or use
# vendor/bin/php-cs-fixer fix --show-progress=estimating --verbose
# or
# ./scripts/fix
# if the -local script throws an error
HASH_AFTER=$(git diff | sha1sum)

if test "$HASH_AFTER" != "$HASH_BEFORE" ; then
  echo "PHP Codestyle was fixed. Please read the changes and retry commit."
  exit 1;
fi

Using docker PHP

Executing the following script will use the dev environment to run the codestyle check. As it currently always runs a new container using docker-compose, it will take some seconds to execute:

./scripts/fix

Using PHPstorm

If you happen to use PHPstorm you can add php-cs-fixer to those settings as well:

PHPstorm enable php-cs-fixer
PHPstorm inspections

Using VSCode

You can use the php cs fixer Extension. It should work right after a restart. To fix a file right click on it and select

VSCode PHP CS Fixer dialog

You can even configure it to fix your code style after saving a file under: Settings>PHP CS Fixer>Execute PHP CS Fixer on save for not commiting any non-fixed code.

Note: You need PHP installed locally for this.

Editorconfig

Depending on your editor you need to do nothing or install or configure a plugin to use the file .editorconfig. Please refer to the section about Code style.

php Code style

We use php-cs-fixer to format the code style. The aim is to make it use the same style as phpstorm does by default. The fixer is based on the @Symfony ruleset, with a few changes.

To format all files, you can run:

vendor/bin/php-cs-fixer fix --show-progress=estimating --verbose

For convenience, you can and should add the code style fix as a pre-commit hook. So you will never commit/push any PHP code that does not follow the code style rules.

There are two possibilities:

Using local PHP

When PHP >= 7.0 is installed locally and the vendor folder is in place (by having used the automated tests or the dev environment), you can use your computers PHP to check/fix the codestyle, as this is the fastest option:

./scripts/fix-codestyle-local

Adding this to .git/hooks/pre-commit could look like that:

#!/bin/sh
HASH_BEFORE=$(git diff | sha1sum)
./scripts/fix-codestyle-local
# or use
# vendor/bin/php-cs-fixer fix --show-progress=estimating --verbose
# or
# ./scripts/fix
# if the -local script throws an error
HASH_AFTER=$(git diff | sha1sum)

if test "$HASH_AFTER" != "$HASH_BEFORE" ; then
  echo "PHP Codestyle was fixed. Please read the changes and retry commit."
  exit 1;
fi

Using docker PHP

Executing the following script will use the dev environment to run the codestyle check. As it currently always runs a new container using docker-compose, it will take some seconds to execute:

./scripts/fix

Using PHPstorm

If you happen to use PHPstorm you can add php-cs-fixer to those settings as well:

PHPstorm enable php-cs-fixer
PHPstorm inspections

Using VSCode

You can use the php cs fixer Extension. It should work right after a restart. To fix a file right click on it and select

VSCode PHP CS Fixer dialog

You can even configure it to fix your code style after saving a file under: Settings>PHP CS Fixer>Execute PHP CS Fixer on save for not commiting any non-fixed code.

Note: You need PHP installed locally for this.

Editorconfig

Depending on your editor you need to do nothing or install or configure a plugin to use the file .editorconfig. Please refer to the section about Code style.

Roles and Permissions

After setting up a local foodsharing instance, you will get to know many workflows and actions that are not available to you on foodsharing itself, due to limited roles and permissions.

With access to both the seed data and the database, those limitations no longer exist! Go and play around with some of the local users, or keep reading for a brief overview and explanation.

Roles

Users have a "quiz role" which signifies the level to which they can help manage the foodsharing platform. The most prominent promotion steps are

  • from Foodsharer to Foodsaver (the "Foodsaver-Quiz"), which eventually allows you to pick up food in cooperating stores
  • from Foodsaver to Store Manager (the "Betriebsverantwortlichen-Quiz"), which enables you to become responsible for cooperations
  • from Store Manager to Ambassador (the "Botschafter*innen-Quiz"), which enables you to become ambassador for regions (districts)

In addition to the above, there are also a few select users with global Orga permissions.

Foodsharer

A Foodsharer can

  • offer food baskets food baskets on the platform or pick them up
  • post information to foodsharepoints (e.g. about current content)
  • join or participate in events
  • see some personal data for other users (pickup count, forum posts, public info, regions & workgroups, wall)
  • edit their own profile and optionally include their address
  • chat with other users and become buddies with them (Ich kenne)
  • receive trust bananas, but not give them
  • receive newsletter mail
  • remove their own account

One becomes Foodsharer simply by creating an account on foodsharing.de or foodsharing.at or ... (depending upon country). A foodsharer can not view or interact with stores. They also cannot join a region, so interacting with the forums is not possible.

Foodsaver

A Foodsaver can

  • join and leave regions (the nightly script makes sure to also add them to parent regions)
  • see stores and apply for membership in a store team, if the team is looking for help
  • sign up to fetch food from stores with empty slots, and sign themself out of future pickup slots
  • create events in regions or workgroups they are part of
  • apply for workgroups and participate
    (in theory they could even administrate the group without having the Store Manager Quiz)
  • view and write in the forum, react to forum posts (large region forums on our platform are moderated)
  • generate their personal business card (once verified), not to be confused with their passport
  • be asked to manage any foodsharepoint in regions which they are part of
  • give and receive trust bananas
  • do a lot more that's not listed here :)

Foodsavers have:

  1. passed the quiz for Foodsaver
  2. signed the legal agreement
  3. correct data (which is verified by the BOT via foodsaver ID)
  4. had some introductory pick-ups (once verified)
  5. a foodsharing passport (once verified, or soon after)

Store Manager

  • can be asked to manage any stores in regions which they are part of
  • has accepted the privacy notice regarding sensitive user data (Vertraulichkeitserklärung)

The following assumes the user is store manager for at least one store, although you can of course have passed the Store Manager Quiz and not be managing any store at the moment.

  • is displayed at the top of "their" store-team list, for people to contact
  • can add and remove store team members, can accept and reject store-team applications
  • can move people to and from waiting list, chat with waiting list
  • can edit the store properties, team status and what's displayed on the foodsharing map
  • can edit the pickup times (repeated ones and one-time pickups)
  • can confirm and reject pickup slots
  • can see the pickup history (past slots)

Ambassador

The following assumes the user is ambassador for at least one region, although you can of course have passed the Ambassador Quiz and not be acting Ambassador at the moment.

  • can manage the profiles of foodsharers and foodsavers with that region as home region (Stammbezirk)
  • can manage the foodsaver passports of people from that region
  • are displayed in the left sidebar of "their" region's forums, for people to contact
  • can perform store manager actions on any stores in that region
  • can manage any local workgroup in the region

Orgateam

  • basically allowed to see and do anything, but remember: with great power comes great responsibility ;-)
  • just browse the pages in the gear-icon menu entry and discover e.g. the internal content management system

Permissions

There are many different things you might manage, for example...

  • a workgroup
  • a store
  • an event
  • a foodsharepoint
  • a region
  • a foodsaver profile
  • a newsletter
  • a poll
  • a foodsaver passport (Ausweis)
  • a blogpost
  • a food basket
  • content in the internal CMS
  • quiz questions

In each of those situations (and there are more!) you'll be allowed or not allowed to perform actions based on the permissions for this context. Hopefully over time, we can briefly outline what's what and which things to look out for!

Many of the computations have already been extracted, and you can find them as part of a Permissions class for the respective module. In several other cases though, the checks for what you are and aren't allowed to do are implicit, meaning they are implemented inline throughout the code. We're trying to extract them wherever possible when working on code nearby, and only introduce "clean" permission handling in new code.

Recently, we have also started introducing special workgroups which can take over certain predefined duties that previously belonged to ambassadors only (or did not exist separately at all). Those so-called workgroup functions currently are:

  • greeting foodsavers who join a region and need introductory pickups (Welcome Group)
  • managing foodshare points of a region (regardless of who's listed as responsible for the point)
  • managing polls for a region (voting is a very new feature, so this is still under construction)

Those groups are optional and right now need to be set up by Orgateam members for a region. Some of them will also show up in the forum sidebar, similar to the ambassador list.

English short version of this file.

Was du hier findest:

Wie du unserem Projekt beitrittst

In der foodsharing-IT organisieren wir unsere Arbeit über slack (slackin.yunity.org). Dafür musst du dir ein Konto bei Slack mit einer funktionierenden Mailadresse anlegen.Im Kanal #foodsharing-dev (auf slackin.yunity.org) besprechen wir, was zu tun ist. Sag da am besten auf englisch Hallo und stell dich dem Team vor - mit deinen Skills und woran du gern arbeiten würdest.

Der gesamte Code liegt im Repository https://gitlab.com/foodsharing-dev/foodsharing. Da gibt es neben der "Project ID: 1454647" die Möglichkeit, auf "request Access" zu klicken. Sag uns in yunity slack im Kanal #foodsharing-dev Bescheid und wir geben dir die Bearbeitungsrechte.

Als Mitglied auf Gitlab kannst du

  • Zweige innerhalb des Repositorys Erstellen und Verschieben (außer Master, mehr zu branches unten)
  • vertrauliche issues sehen
  • Labels vergeben (super nützlich!)
  • dich issues zuordnen (um anderen zu sagen, dass sie nicht damit anfangen müssen)

Zum Mitarbeiten brauchst du dann noch deine lokale Programmierumgebung (https://devdocs.foodsharing.network/getting-the-code.html).

Wie du einen Starter Task findest

Mit welcher Aufgabe du anfangen könntest, hängt natürlich von den Vorkenntnisse ab. Frag am besten in Slack nach, was ein guter starter task wäre.

Du kannst natürlich auch selbst etwas aussuchen (https://gitlab.com/foodsharing-dev/foodsharing/issues, gib als Label "starter task" an).

Ordne dich bitte dem Issue zu, das du bearbeitest. (rechts oben im issue: assign yourself)

Tutorials

Tutorial-Empfehlungen für den Umgang mit Git

  • Für die basics: http://rogerdudler.github.io/git-guide/
  • Für Rebases etc.: http://think-like-a-git.net, https://git-scm.com/doc und https://learngitbranching.js.org/

Auf welchen Ebenen Git funktioniert

Ebene eins: Deine Arbeitsumgebung. Wenn du Sachen änderst, wird erstmal nix hochgeladen.

Sobald du anfängst, Änderungen zu schreiben, empfiehlt es sich, einen Branch zu erstellen.

Wie bei einem Baum ist der Zweig etwas, was von dem Stamm abgeht. Alle haben den Master, aber erstmal hast nur du deinen Branch.

https://confluence.atlassian.com/bitbucket/branching-a-repository-223217999.html

Ebene zwei: Du möchtest, dass auch andere deinen Branch sehen und darüber philosophieren können. Dafür musst du deine Änderungen bestätigen, to commit.

Damit hebst du die Dateien einzeln auf die Upload-Ebene. Sobald du anfängst, zu committen, lohnt es sich einen Merge Request zu erstellen.

Den Merge Request erstellst du im Gitlab selbst: https://gitlab.com/foodsharing-dev/foodsharing-android/-/merge_requests/new

Der MR sagt aus: "Ich arbeite daran und werde in absehbarer Zeit fertig". Solange es noch ein Work in Progress ist, solltest du den MR auch mit WIP am Anfang benennen.-> https://devdocs.foodsharing.network/contributing.html

Sobald der MR fertig ist, benennst du ihn um, bittest im Kanal um ein review, und wenn jemand gesagt hat: Yo, läuft, dann kann der Merge Request gemerged werden, also mit dem Master zusammengeführt werden. Dafür musst du in der Regel erst deinen Zweig lokal mit dem Master zusammenführen: rebase.

Wenn ein MR übernommen ist, hat dein Code ebene Drei erreicht und alle, die sich das repository ziehen, haben ihn ebenfalls im Master.

Ebene vier ist dann, wenn von der Test/Beta-Version in die produktive Version rübergeschaufelt wird.

Wenn du Probleme hast, frag am besten jemand im Kanal, ob er/sie dir helfen kann, die Git-Kommandos in der richtigen Reihenfolge auszuführen.

Workflow

Ein Workflow kann so aussehen: TL/DR: Branch erstellen -> ein oder mehrere commits machen -> pushen -> MR erstellen

git checkout master
git pull
git checkout -b 123-fix-for-issue-123

(Erstellt den Branch 123-fix-for-issue-123. Es ist gut, wenn du die Issue-Zahl vorne an deinen Branch setzt, damit wir sehen können, wozu er gehört. Du kannst gerne genauer beschreiben, worum es geht: git checkout -b 812-1-make-devdocs-contributing-a-german-text)

Anschließend an Dateien arbeiten.

git add src/EditedFile.php

(Fügt die bearbeitete Datei EditedFile.php zu denen hinzu, die hochgeladen werden sollen. Es ist empfehlenswert, git add -p path_to*_*file zu benutzen, damit du genau weißt, was du zum Commit hinzufügst.)

git status ODER git diff --staged

(Nützlich, um sich vor dem commit anzuschauen was gerade der Status ist oder was man gerade in seiner staging area hat und was genau zu dem aktuellen commit hinzugefügt wird)

./scripts/fix-codestyle-local (oder, wenn das nicht funktioniert, benutz das langsamere ./scripts/fix) Damit überprüfst du deinen Code style.

./scripts/test Damit lässt du lokal die Tests durchlaufen. Für spätere Wiederholungstests nutze: ./scripts/test-rerun (viel schneller!)

git commit -m "Changed something in EditedFile.php"
git push --set-upstream origin 123-fix-for-issue-123

(Bündelt die Änderungen mit der öffentlichen Notiz "..." und lädt sie hoch. Es kann sein, dass ein Kommando vorschlägt, das noch zu geben ist.

Insbesondere bietet es sich hier an, einen MR für den branch zu erstellen und ihn als Work in Progress (WIP) zu markieren.)

Anschließend weiterarbeiten

git add src/AnotherFile.php
git commit -m "Changed something in AnotherFile.php"
git push

// This works now//

Wenn du das Changelog bearbeitet hast:

git add CHANGELOG.md
git commit -m "Updated Changelog"
git push

(Nach git push kann man als Kommando -o ci.skip hinzufügen. Das überspringt Tests, was etwas Energie spart. Tests sind am Ende allerdings wichtig, wenn alle Dateien fertig sind.)

Wenn du fertig bist:

git checkout master
git pull
git checkout 123-fix-for-issue-123
git rebase master
// Depening of the age of your branch and other changes, this will just work or require some more work (follow instructions by git)

git push -f 
// force push will overwrite changes on origin, since rebasing changed the history of your branch

Was es zum Merge Request zu sagen gibt

Je übersichtlicher und besser beschrieben der Merge Request (MR) ist, desto einfach ist es für andere, ihn zu reviewen. Das heißt:

  • Gib dem MR einen aussagekräftigen Namen, verwende bitte ein Template für die Beschreibung ("default" aus der dropdown-Liste) und beschreibe, was die Änderung macht.

  • Wenn es eine Änderung an der UI ist, füg gerne auch ein paar Screenshots ein.

  • Geh einmal die Checklist (ganz unten im Template) durch und schau, ob du alle Punkte beachtet hast, bzw. Punkte nicht zutreffen. Beispielweise ist nicht immer ein Test notwendig oder möglich. Die Ausnahme ist der letzte Punkte ("Once your MR has been merged...") der das Testen nach dem mergen betrifft (s.u.)

  • Gib dem MR noch ein paar Label, die ihn einordnen. Insbesondere ein "state"-Label hilft zu sehen, ob der MR fertig ist.

Unten im MR kann dieser diskutiert werden. Anmerkungen am Code kannst du am besten unter "Changes" direkt an der entsprechenden Zeile einfügen.

Wie du einen Rebase machst

  1. Du holst Dir mit git checkout master und git pull die aktuellen Änderungen vom master Branch.
  2. Wechsele danach wieder mit git checkout BRANCHNAME in deinen branch.
  3. git rebase master ist der Befehl deiner Wahl. (Den Unterschied zwischen rebase und merge findest du hier: https://git-scm.com/book/en/v2/Git-Branching-Rebasing)

Falls du das Programm phpstorm nutzt, klickst du rechts unten auf deinen Branchnamen. Dadurch geht ein Menü auf, in dem du den Master auswählst und anklickst: "checkout and rebase onto current".

  1. Sollte das rebasen zu kompliziert sein oder nicht funktionieren: Fallen lassen wie eine heiße Kartoffel ;-) mit dem Befehl "git rebase --abort" Führe stattdessen ein merge durch, da dies einfacher ist, da nur die Änderungen von master branch eingefügt werden. Hierzu kannst du den Befehl git merge master anwenden.

In phpstorm gibt es zwei Menüpunkte unter dem obigen: "Merge into current". Du findest unten rechts eine Möglichkeit, dir die Versionsunterschiede anzeigen zu lassen. Mit dem Zauberstab-Knopf oben kannst du konfliktfreie Änderungen automatisch vornehmen lassen. "ours" und "theirs" entspricht den Pfeilen am Diff-Rand. (... Hier könnte jemand irgendwann Screenshots einfügen ...)

  1. Danach kannst du mit einem git commit und git push die Änderungen hochladen, wenn du alle Konflikte bereinigt bekommst.

Wie du mit Rebase-Konflikten in unseren Abhängigkeiten umgehst

(https://stackoverrun.com/de/q/11809185)

Hast du in deinem Branch Änderungen an der composer.json und / oder client/packages.json durchgeführt und gleichzeitig hat jemand auch an diesen Dateien Änderungen in den master gemergt, kommt es in composer.lock und yarn.lock zu einem Konflikt.

  1. Führe den Befehl git checkout master -- chat/yarn.lock, client/yarn.lock or composer.lock aus 2. Danach loggst du Dich mit ./scripts/docker-compose run --rm client sh in den Docker-Container "Client" ein. 3. Führe darin den Befehl yarn aus und beende mit exit, wenn dieser fertig ist. (bzw. für composer wäre es ./scripts/composer install) 4. Danach kannst du mit einem git add chat/yarn.lock, client/yarn.lock or composer.lock und git rebase --continue den Rebase fortsetzen.

(Auf der Rebase-Seite gibt es auch ein Beispiel.)

Merge in den Master

Wenn du fertig bist, meldest du dich im Slack-Dev-Kanal und schreibst da einen Link zu deinem MR hin, mit der bitte um Rückmeldungen. Andere Devs werden sich dann mit Feedback oder Änderungswünschen an Dich wenden. Bitte habe etwas Geduld, wenn das nicht sofort passiert.

Sobald der/die Genehmigende deinen MR für fertig befindet, wird er ihn in den Master übernehmen.

Der Master-Zweig wird automatisch auf beta.foodsharing.de bereitgestellt, wo er getestet werden kann. im Slack-Beta-Kanal solltest du nochmal drauf hinweisen, dass sich etwas geändert hat und getestet werden möge. Dort gibt es dann noch einmal gegebenenfalls Fehlermeldungen. (Besser hier als auf www.foodsharing.de! :-) )

... für einen Überblick über verschiedene Umgebungen: environments on GitLab

Am Ende werden die beta-Änderungen auf die produktiv-Seite übernommen - und dein MR ist abgehakt.

Und dann ... Zeit für eine neue Aufgabe ...?

Wenn du eine Frage hast, erreichst du uns über yunity slack: komm in den Kanal #foodsharing-dev.

Ein issue anlegen

Wenn dir etwas an der foodsharing-Webseite aufgefallen ist: prüf bitte, ob das auf beta.foodsharing.de und www.foodsharing.de auftaucht.

  • www und beta = es ist ein unbearbeitetes Verhalten = prüf bitte, ob das Issue bereits bei GitLab eingetragen ist: issues ... wenn es noch nicht existiert: leg das issue bitte mit möglichst genauen Informationen an
  • www und nicht beta = wir haben uns schon drum gekümmert
  • nicht www aber beta = wir haben es verursacht = bitte im Slack-Beta-Kanal melden

Testen

Du kannst die Tests mit ./scripts/test bzw. ./scripts/test-rerun durchführen (siehe oben). Solange wir die Tests so schreiben, dass sie idempotent ablaufen, nutz bitte test-rerun!

Bis jetzt funktionieren die Ende-zu-Ende-Tests (in der Codeception acceptance test genannt) gut. Sie laufen mit einem kopflosen Firefox und Selenium innerhalb des Dockers und sie werden auch auf CI-Basis ausgeführt.

Wir sind dabei, den Code umzustrukturieren, um Unit-Tests zu ermöglichen: incremental refactor.

Der während des Testens erzeugte Zustand wird nicht weggeworfen, und du kannst die Test-App nutzen: im Browser, und es hat seinen eigenen phpmyadmin.

Wenn Du die Tests mit eingeschaltetem Debug-Modus durchführen willst, verwende ./Skripte/Test --Debug.

Wenn Du nur einen Test ausführen willst, gib den Pfad zu diesem Test als Argument an, z.B: ./Skripten/Tests/Abnahme/LoginCept.php.

Ausführlicheres zu Tests findest du hier.

German long version for this file.

Contributing

If you have any questions please reach out to us via slack: yunity slack and join the #foodsharing-dev channel.

Submitting an issue

If you found an issue on the foodsharing website, then please submit it to our GitLab issues.

If you feel comfortable submitting a fix (or if you like to try ;) ) too, then follow the next section.

Submitting a change

Becoming a member

As an “member” on Gitlab you can

  • create and push to branches within the repository (except master)
  • see confidential issues
  • set labels to issues
  • assign yourself to issues (to tell others that they do not need to start on them) After creating a Gitlab account and applying for membership, write a few introducing lines about you on the Slack channel yunity slack #foodsharing-dev. You can apply for membership by clicking the Request Access Button in the GitLab UI, after you created your account.

Working on an issue

You can either submit your own issue and work on it or work on existing issues. Issues that are suitable for newcomers are labeled as starter tasks.

To work on an issue:

  1. Check if there is an issue for the change in the GitLab issues.
  • This is a seperate project as it is public and the repo is not.
  • If you are just submitting a very small change or a doc fix, then don't worry about creating an issue.
  1. Create a new git branch, prefixed with the issue number rather than fork the repo, as it makes permissions trickier.
  • For example, the issue number 56 would have a branch named 56-some-descriptive-words.
  • Optionally, add your name to the branch name; for example, 56-nicksellen-some-descriptive-words.
  1. Make your changes and push them. If they are very small or only documentation you can consider using the push option git push -o ci.skip which disables running the build and test on the Gitlab server.

To submit your change:

  1. Check if the code style is fixed before commiting, by running ./scripts/fix-codestyle-local (or if that does not work by running the slower ./scripts/fix).
  2. Check if the tests pass locally, by running ./scripts/test.
  3. Create a merge request to master for your branch early on.
  4. Select the template "Default".
  5. Prefix the name of the merge request with WIP:.
  6. Make sure your merge request checks all the checkboxes in the "Default" template (and check them in the description).
  7. Once you think your branch is ready to be merged, remove the WIP: prefix from the name of your merge request. Rebase your branch onto master (which might have developed since your branching). It is OK to force-push (git push origin <your_branch_name> -f) after rebasing.
  8. Submit your merge request.

The next steps will be:

  • An approver will get back to you with feedback or change requests. Please have some patience if this does not happen right away.
  • Once the approver considers your changeset ready to be made, they will merge it into the master branch.
  • The master branch will be deployed automatically to beta.foodsharing.de, where you can try it out (uses production database).
  • Hang around and see if people in #foodsharing-beta on Slack find any issues, etc.
  • At some point in the future, once a few changes have been collected, they will all be deployed to production.

Testing

You can run the tests with ./scripts/test, for your second and following runs, you can use ./scripts/test-rerun which runs much quicker (as long as we keep writing the tests to run idempotently, please do!).

So far, end to end tests (called acceptance tests in codeception) work nicely. They run with a headless Firefox and Selenium inside the Docker setup and they are run on CI build too.

We are restructuring the code to enable unit testing.
Related issue: Incremental refactor.

The state created during testing is not thrown away, and you can visit the test app in your browser, and it has its own phpmyadmin.

If you want to run the tests with debug mode turned on, use: ./scripts/test --debug.

If you just want to run one test, pass the path to that test as an argument, e.g.: ./scripts/test tests/acceptance/LoginCept.php.

Frequently asked questions - what you can find here

Basic layout

Q: The architecture is not clear to me. Could you explain the basic layout?

A: The main architecture goals came from this book "Modernizing Legacy Applications In PHP", although we deviate in some place. There is also a great 1h video describing rough details about what the book is talking about.

A few current architecture goals would be:

  • only 1 php entrypoint (index.php), remove xhr|xhrapp.php/etc
  • use symfony approach for handling all requests (some controllers do this already, others use the old way), in IndexController the $isUsingResponse switches behaviour based on that
  • create REST controllers for all API use, remove all other API stuff
  • use a symfony router or similar (might also involve changing page urls to nice paths instead of get parameters + adding redirects)
  • have simple/general/default kind of symfony index.php for templates/frontend stuff
  • remove all html/js in php strings
  • use vue.js for all dynamic kind of templates
  • remove global eval stuff ... goes with the only REST API endpoints ...
  • modernize a lot more of the frontend code
  • replace years long outdated flourish lib

So the preferred approach would be Model to Gateway classes, see here: Issue 9

Getting started

Q: I would really love to do anything, but when I look at the repo I can't even find a thing I could change just for testing! How do you start?

A: One technique is to

  1. find some text that is clearly visible on the page, and
  2. search the codebase for it, which might point to a translation string, then
  3. search for that translation key,
  4. repeat until finding it.

In foodsharing some of the longer content comes from the database, so you won't find that in the the codebase (except for maybe in an sql seed file), so try things more like buttons, or menu items...

Technical constraints

Q: What are our technical constraints? (such as server, storage. memory and communication interfaces)

A: The server has 32G of memory, currently. Server stats:

The current email load is high, we get spam-flagged a lot, so in the future we need to introduce (more) granular email settings to users. (E-mail handling: we have currently more than one server for mails.) When we use a third party service, we sometimes run into problems (photon, map tiles).

visual guidelines

Q: Are there visual guidelines? (User interface, colors, buttons, etc.)

A: There are guidelines. Basically, we use common sense.

The frontend needs rewriting as well and currently we're mostly working on & refactoring the backend.

... it could look like Karrot

security parameters

Q: What are the security parameters? (i.e. how the website will ensure secure access control and transactions)

A: There were a lot of SQL injections. Now we use prepared statements, which is clearer. Vue helps against XSS attacks. (there might be legacy stuff lurking)

Moving (from XHR to REST APIs made it safer as well.

We're aiming for a point, where you get only the data you're requesting (Currently a lot of code is structured "if you're this or that, you get to see xy")

Structure

Q: So if we break the homepage down into its parts - how is it structured?

A: For some of the php code, see the php page.

Most of our functions can be found in /src/Helpers/.

Book: https://leanpub.com/mlaphp

We still have some of the functions that bit by bit get replaced during refactoring.

karrot

Q: What is the relationship between karrot and foodsharing currently?

A: karrot enables communities to do foodsaving - foodsharing is basically in german and has a lot of structures that some countries didn't want (thus e.g. minimizing the use of admins in the groups) ... the german-speaking organizational structures are kinda rooted in the code of foodsharing.

foodsharing is ALSO an organization.

karrot is a software project, the people who use it are their own organization, in a way.

Chandi showed how karrot and foodsharing might work together on the code base. (https://yunity.slack.com/archives/C1T1ZM9A4/p1577146381053600) For now, we're focusing on modernizing the codebase.

our tech

Q: it-tasks.html lists a number of tech stuff we use on the page. How is their relationship to each other / what do we use them for? What do we want to remove from our codebase?

A: We use ...

  • PHP (Symfony 5) ... we also use a lot of non-symfony-php, that we want to refactor
  • JavaScript (Webpack, Vue.js) ... we're also modernizing some old JavaScript. Vue impacts mainly just one thing. CSS mostly has an effect on other stuff as well. (see below)
  • HTML (Twig) ... there is also the old way with string contatination. Twig is the new way. We're moving more towards vue.
  • CSS (Bootstrap) ... move from global CSS to vuevue components
  • MariaDB (which is basically MySQL) currently has a very lax error handling configuration. We try to set a more strict config here.
  • Redis is also very low maintenancy. It's a cache, where the session is stored.
  • socket.io nodejs server - the chat server. Low maintenance.
  • RESTful APIs - We also have old ones but are moving towards REST.
  • the Docker Compose development environment
  • Codeception for Unit-, API-, and Acceptance testing (with Selenium) -
  • Git
  • GitLab CI for tests and automatic deployment (with php deployer) - this is a nice, stable setup through which multiple people can deploy stuff. (Not bottle-necking through one person as jobs are usually splitted between at least two servers.)

helping hands

Q: Who can I ask for help with what? Who is part of the team with which focus and which skills? (volunteer list)

A: The common and most efficient way is to ask the dev channel a detailed question - so everyone who might have the knowledge and time can help.

Also we have team members with special responsibilities, see here: https://gitlab.com/foodsharing-dev/foodsharing/-/wikis/responsibilities

This means that there currently is no one head of the foodsharing IT. We decide with votes & vetos. Therefore currently there is no roadmap. The responsibles have lately said, that cleaning up old code and finishing open Merge Requests has priority over new features. But basically if you like an idea and are willing to work on your code - you're welcome to join. :-)

Coding guidelines

We do have a few coding guidelines, mentioned at various places in these devdocs. In general, please use your common sense and make it fit with the existing code.

Code style

Why

First of all, why do we need a code style: If the code looks everywhere the same, it is easier to read and thus to maintain. You are getting kind of a style, a common syntax everybody can understand. You can even distinguish library code from our own code like this. There are also some pedantics who like such a clean overall look. But most important thing is that nobody wants to get reviews saying “too many blank lines”, “please indent by spaces”, “The brace should be on the next line”. In short, no review on anything regarding white space.

Additionally, we do not want to have to review huge amounts of code with such a diff:

Indentation change diff

if you see this in pure diff style (and are not able to read the matrix without a GUI), this becomes very cumbersome.

Editorconfig

The cool thing is, as we now all love code styling, but everybody hates code style fixing, we do automate all such things. The file /.editorconfig (see [https://editorconfig.org/]) specifies our most basic settings on white-spacing which most of your IDEs will be able to follow. Editorconfig is a file format and collection of text editor plugins for maintaining consistent coding styles between different editors and IDEs.

PHPstorm

In PHPstorm this is how you select it: use the corresponding plugin:

Plugin EditorConfig in PHPstorm

For the editorconfig file format there are super nice inspections in PHPstorm (under Editor-Inspections after searching for editorconfig). Leave them all in. The more annoying, the better.

VSCode

EditorConfig Plugin for VSCode

To use EditorConfig, install the official EditorConfig for VSCode Extension. It will automatically use the proper .editorconfig file from the repository.

Testing

Running tests

Run the tests with:

./scripts/test

You will need to have initialized everything once (with ./scripts/start), but you do not need to have the main containers running to run the tests as it uses it's own cluster of docker containers.

After running the test, you can stop it with FS_ENV=test ./scripts/stop. If you don't, the docker containers keep running and need resources. With this, you can set the FS_ENV environment variable to test, so they operate on the test environment. Also it is possible to add this in the config file. Maybe some day this info gets added. :-)

After you have run the tests once, you can use ./scripts/test-rerun which will run faster. It assumes that the containers have already been created and initialized, but otherwise is the same.

So far, end to end testing is working nicely (called acceptance tests in codeception). They run with a headless Firefox and Selenium inside the Docker setup, they are run on CI build too.

We are working on restructing the code to enable unit testing.

The test contains stay around after running, and you can visit the test app in your browser, and it has it's own phpmyadmin.

If you want to run with debug mode turned on, then use: ./scripts/test --debug.

If you just want to run one test, then pass the path to that test as an argument, e.g. ./scripts/test tests/acceptance/LoginCept.php.

Writing unit tests

CodeCeption uses PHPUnitTests under the hood and therefore the PHPUnit test documentation can be helpful.

Writing acceptance tests

The tests directory has much stuff in it.

You just need to care about 2 places:

tests/seed.sql - add any data you want to be in the database when the tests are run acceptance/ - copy an existing test and get started!

http://codeception.com/docs/modules/WebDriver#Actions is a very useful page, showing all the things can call on$I. Please read the command descriptions carefully.

How acceptance tests work

Tests are run through selenium on firefox. The interaction of the test with the browser is defined by commands. Keep in mind that this is on very high level. Selenium does at most points not know what the browser is doing!

It is especially hard to get waits right as the blocking/waiting behaviour of the commands may change with the test driver (PhantomJS, Firefox, Chromium, etc.).

$I->amOnPage

uses WebDriver GET command and waits for the HTML body of the page to be loaded (JavaScript onload handler fired), but nothing else.

$I->click

just fires a click event on the given element. It does not wait for anything afterwards! If you expect a page reload or any asynchronous requests happening, you need to wait for that before being able to assert any content.

Even just a javascript popup, like an alert, may not be visible immediately!

$I->waitForPageBody()

can be used to wait for the static page load to be done. It does also not wait for any javascript executed etc.

HtmlAcceptanceTests

Acceptance tests using the HtmlAcceptanceTester class are run in PhpBrowser. Those tests run on a lower level then WebDriver. They can only test a page's HTML content. Therefore features like JavaScript are not available, but tests run faster.

From Codeception documentation:

HtmlAcceptanceTesterAcceptanceTester
JavaScriptNoYes
see/seeElement checks if text is……present in the HTML source…actually visible to the user
SpeedFastSlow

Code quality and reviews

The main goal of your contribution to the foodsharing codebase should be to make the platform great for the users. Still, a very important aspect of that is to make the codebase great for developers as well so others can help with making it great for the users. That is why you should have a second goal with each commit: Make the code a little bit nicer than it was before. Below is a list of things that should be kept in mind when touching code but also when reviewing. When you think there is something touched that might break one of the points listed below, better delay the approval of the merge request or ask another person for a review.

What you should care about specifically

If you take responsibility it's okay to break master. Please try not to break it horribly :-D

We welcome new and beginner developers to contribute to foodsharing and understand that part of that might involve accidentally breaking bits of the site. And that is okay, as long as they stick around to fix what they broke. Still, try to be aware of what you are touching:

  • Do not break things that affect non-beta users
    • Email notifications generated by actions of beta users are send to everybody
    • Modification of data, especially in stores, forums and walls, affect everybody as the content is shown on beta and production
    • An accidental loss of data is the worst case

Do not introduce security issues again

  • Never write any new code using Foodsharing\Lib\Db class, always use Foodsharing\Modules\Core\Database with prepared statements
  • When refactoring, take one step at a time. A lot of old code uses strip_tags as a basic Cross Site Scripting prevention method, it is hidden behind strval. Keep it when moving code.
  • Always be aware what type of data is held in a variable: Plain text, HTML text, markdown? The old code does mostly not do this and is not even aware of the type when outputting it to the user. Still, when you want to change that behaviour, you must be aware of every single instance of that string used over the platform (e.g. it might be stored to the database or session and retrieved at other places). If in doubt, first try to leave that behaviour exactly as you found it and refactor as a separate step

REST API Endpoints

In the issue #511 some rules for creation of REST API Endpoints are formulated. For general explanation about REST, see request types.

  1. english only
  2. use "normalizer" methods to transform gateway/db data into api responses
  3. camel case for keys (regionId instead of region_id)
  4. prefixes for booleans (isPublic instead of public)
  5. GET requests should never change data
  6. use Permission classes for permission checks
  7. never use Model classes
  8. regions and working groups are both 'groups'
  9. name keys always as specific as possible (createdAt instead of time, author instead of user)
  10. integers should also be send as an integer, not as a string
  11. Standardize date and time: ISO 8601. Use the DATE_ATOM PHP DateTime formatter.
  12. Add a message to exceptions. (e.g. throw new HttpException(404, 'This region with id ' . $regionId . ' does not exist.');)

More not-yet-implemented ideas include:

  1. Add API versioning (to allow introducing breaking api changes in the future without immediately breaking the apps) (not yet, hopefully coming at some point)
  2. Standardize pagination (e.g. fixed query param names, return total number of items, either via envelope or header)
  3. Automatically generated documentation for REST API

Contents

How you perform a rebase

  1. You get the current changes from the master branch using git checkout master and git pull.
  2. Change back to your branch with git checkout BRANCHNAME.
  3. git rebase master is the command of your choice. (You can find the difference between rebase and merge here: https://git-scm.com/book/en/v2/Git-Branching-Rebasing)

If you use the phpstorm program, click on your branch name below. This will bring up a menu where you select the master and click "checkout and rebase onto current".

  1. if the rebase is too complicated or does not work: Drop it like a hot potato ;-) with the command "git rebase --abort" Do a merge instead, as this is easier as only the changes from master branch are inserted. You can use the git merge master command for this.

In phpstorm there are two menu items under the above: "Merge into current". You will find a way to see the version differences in the lower right corner. With the magic wand button at the top you can have conflict-free changes made automatically. "ours" and "theirs" correspond to the arrows on the diff border. (... Someone might insert screenshots here at some point...)

  1. After that you can upload the changes with a git commit and git push, if you manage to fix all conflicts.

How to deal with rebase conflicts in our dependencies

(https://stackoverrun.com/de/q/11809185)

If you have made changes to composer.json and / or client/packages.json in your branch and at the same time someone has merged changes to these files into the master, a conflict will occur in composer.lock and yarn.lock.

  1. Execute the command git checkout master -- chat/yarn.lock, ... client/yarn.lock or ... composer.lock
  2. Then log into the docker container "client" with ./scripts/docker-compose run --rm client sh.
  3. Execute the command yarn and finish with exit when it is finished. (For composer it would be ./scripts/composer install.)
  4. Then you can continue the rebase with a git add chat/yarn.lock, client/yarn.lock or composer.lock and git rebase --continue.

Rebase on console with an example

If your rebase in the console looks like this ...

git rebase master

Auto-merging src/Modules/Profile/ProfileView.php
CONFLICT (content): Merge conflict in src/Modules/Profile/ProfileView.php
Auto-merging lang/DE/de.php
Auto-merging lang/DE/Settings.lang.php
error: could not apply 62feb08c3 ... make use of "about_me_public" as described on profile page.
... then resolve all conflicts manually, mark them as resolved with:
"git add/rm <conflicted_files>", then run "git rebase --continue",
You can instead skip this commit: "run git rebase --skip".
To abort and get back to the state before git rebase, run "git rebase --abort".
Could not apply 62feb08c3 ... make use of "about_me_public" as described on profile page

then open the conflicting file with an editor of your choice. It will have the HEAD marker somewhat like this:

<<<<<<< HEAD
======= 
/**
     * @param array $infos
     *
     * @return array
     */
    private function renderAboutMePublicInformation(array $infos): array
    {
        if ($this->foodsaver['about_me_public'])
        {
            $infos[] = [
                'name' => $this->translator->trans('foodsaver.about_me_public'),
                'val' => foodsaver['about_me_public'],
            ];
        }
        return $infos;
    }
    /**
     * @param array $infos
     *
     * @return array
     */
     >>>>>>> 62feb08c3... make use of "about_me_public" as described on profile page

... change this to what you'd like it to say. Remove the <<< and >>> and === lines.

/**
     * @param array $infos
     *
     * @return array
     */
    private function renderAboutMePublicInformation(array $infos): array
    {
        if ($this->foodsaver['about_me_public'])
        {
            $infos[] = [
                'name' => $this->translator->trans('foodsaver.about_me_public'),
                'val' => $this->foodsaver['about_me_public'],
            ];
        }
        return $infos;
    }
    /**
     * @param array $infos
     *
     * @return array
     */

Save.

git add FILENAME
git rebase --continue

Troubleshooting

During development everyone knows the moments when the code shows exactly what you want but the computer just does something different. Some strategies how to find or avoid errors are collected here.

Nightly scripts

With ./scripts/docker-compose run --rm --no-deps app php -f run.php Maintenance daily you can call the nightly scripts.

Test artifacts

During the Gitlab CI tests are run at every push. These builds and tests can be found under the menu item CI/CD (the rocket). There you can already see, which stages (build, test, deploy) passed. In order to get the detailed test results, go to the job test in the suitable pipeline (also reacheable via the corresponding MR if existing), click „Browse“ and navigate to tests/_output/_output/report.html (https://gitlab.com/foodsharing-dev/foodsharing/-/jobs/<job number>/artifacts/browse/tests/_output/_output/report.html).

CSRF Exception

When working on the API one usually wants to try it out. If you just type in the API call in the web browser while running the local webpage on localhost:18080 you probably get a CSRF Exception. This is a safety feature:

  • While you are logined via foodsharing.de other pages can send API calls.
  • Since your browser has a session foodsharing.de usually would answer the request, the other page got data that it shouldn't get.
  • Solution: foodsharing.de sends a CSRF-token that the browser saves as a cookie and the client reads from the cookie and sends the token as a header with every API call. Since cookies can only be accessed by the correct web page, only the foodsharing.de site can make requests.
  • When you just type in the API call the headers including the CSRF-token are not set and you are rejected.

There are several work-arounds:

  • You write tests. You should write tests anyway and since they emulate a complete session, the CSRF-Token is sent and valid.
  • You add an API call in some javascript-file that gets executed. For example add the following into /src/Modules/Dashboard/Dashboard.js:
import { get } from '@/api/base'
get('/activity')

Make sure that you do not commit those temporary changes!

  • You disable the CSRF-Check in /src/EventListener/CsrfListener.php by commenting the lines
// if (!$this->session->isValidCsrfHeader()) {
//  throw new SuspiciousOperationException('CSRF Failed: CSRF token missing or incorrect.');
//}

Make sure that you do not commit those temporary changes!

Restart

Sometimes the docker container get into some weird state. It might help to restart them:

./scripts/stop
sudo ./scripts/clean # sudo necessary since the container run with root privileges and therefore create directories with root ownership
./scripts/start

But it takes quite a while.

Cache

Symfony that is running inside docker container are using a cache directory that is persistent over docker restarts and sometimes changes in the source files are not reflected in the running containers. Then errors that are already fixed might still appear during experiments. Hence sometimes it helps to remove the cache directory:

sudo rm -rf ./cache/dev

or even sudo rm -rf cache.

Restart, clean and delete cache - Quick and dirty ;-)

./scripts/stop && sudo rm -rf cache && sudo ./scripts/clean && ./scripts/start

This deletes and restarts the docker containers.

Database and email access

The local website gives you database access so that you can directly view and modify what is written in the database. Access to the e-mails that are sent via the website can also be found.

devtest
Websitelocalhost:18080
phpadmin (database access)localhost:18081localhost:18080
smtp (outgoing email)localhost:18084localhost:28084

Those ports are configured in /docker/docker-compose.*.yml.

Logs

The server (also the local one) writes logs about a lot that happens including errors. To view those logs, run

./scripts/docker-compose logs -f app

where you can also replace app by other components of the application that are listed by ./scripts/docker-compose ps or just remove it to show all logs.

docker-compose also respects the variable FS_ENV that can be set to dev or to test for running either the localhost (dev) containers or the testing containers.

In order to print specific information in the logs, you can print them in your php-code. In order to do so, add a LoggerInterface in the constructor __construct:

use Psr/Log/LoggerInterface;
...
  private $logger;
...
  public function __construct(<other params>, LoggerInterface $logger) {
...
    $this->logger = $logger;
  }
...
// somewhere in your tested, executed code:
    $this->logger->error('some error text');
// especially useful if put into an except clause that catches all errors and reraises them after printing some informative message

Beta Testing

Alle Neuerungen und Fehlerbehebungen, die auf Foodsharing.de online gehen, durchlaufen vorab eine Testphase auf beta.foodsharing.de. Das Beta-Testing dort ist extrem wichtig für uns, da nur so vermieden werden kann, dass wir Fehler auf die Gemeinschaft loslassen und unter Umständen die Seite, die Datenbank oder sogar den ganzen Ablauf beschädigen. Ihr könnt durch frühes Finden und Melden von Fehlern allen Nutzern (und insbesondere auch den Programmierer*innen) das Leben angenehmer machen. Keiner will nach dem Release einer neuen Live-Version die Nacht durchmachen, wenn auf einmal die Fehler dann erst deutlich werden.

Praktischer Nebeneffekt: Auf der Beta-Seite könnt ihr nützliche Neuerungen teilweise schon viele Wochen früher nutzen.

So unterstützt ihr uns beim Testen

  1. Die gesamte Kommunikation rund ums Testen findet im Messenger Slack statt. Registriere dich dafür bitte unter slackin.yunity.org (das geht schnell und kostenlos) und installier anschließend die App (gibt’s auf slack.com für Windows, macOS, Linux, iOS und Android). Auf Slack gibt es für verschiedene Themen unterschiedliche Channels, der wichtigste für dich heißt #foodsharing-beta – hier bitte beitreten. Im Beta-Kanal kommunizieren wir auf Deutsch, an denen meisten anderen Orten auf Englisch.
  2. Im besagten Channel melden sich Entwickler*innen mit Testaufträgen (im Normalfall immer dann, wenn gerade eine Änderung an der Seite fertig geworden ist). Dein Job: Die beschriebene Funktion auf beta.foodsharing.de ausprobieren und Rückmeldung geben: Wenn alles klappt wie erwartet, reicht ein kurzer Satz. Sollte es Probleme geben, beschreibe bitte, was du getan hast und was nicht funktioniert hat. Wichtig: Gib nach Möglichkeit zu allen Testaufträgen Rückmeldung, auch wenn das manchmal ein bisschen Arbeit bedeutet – nur so bekommen wir ein vollständiges Bild und finden alle Probleme.
  3. Du kannst beta.foodsharing.de ausschließlich für Testaufträge oder auch dauerhaft nutzen. Beide Versionen der Seite greifen auf die gleichen Daten zu und bieten (bis auf neue Features und Fehlerbehebungen) die gleichen Funktionen. Aber: Obwohl wir grundsätzlich versuchen, nichts kaputt zu machen, können sich Fehler einschleichen. In einigen Fällen (und gerade dann, wenn etwas nicht wie gewollt funktioniert) ist es daher sinnvoll, auf foodsharing.de auszuweichen.

Und noch ein paar wichtige Punkte

  1. Damit es nicht unübersichtlich wird, arbeiten wir in Slack mit Threads: Über das Sprechblasensymbol könnt ihr direkt auf eine Nachricht antworten, also zum Beispiel auf einen Testauftrag. Auf diese Weise sind alle Informationen an einer Stelle gebündelt und schnell auffindbar. (Eine genaue Erklärung findet ihr hier.)
  2. Den Kanal #foodsharing-beta könnt ihr auch nutzen, um auf Probleme aufmerksam zu machen, für die es keinen Testauftrag gibt oder die ihr keinem Auftrag zuordnen könnt. Wichtigstes Kriterium für eine Fehlermeldung: Auf beta.foodsharing.de ist es kaputt, auf foodsharing.de nicht.
  3. Unter jedem Testauftrag findet sich ein Link zu gitlab.com, dem Portal, auf dem die Entwicklung koordiniert wird. Hier finden sich oft noch mehr Informationen zur jeweiligen Änderung.

Noch tiefer einsteigen?

Auch über das Bearbeiten von Testaufgaben hinaus könnt ihr uns unterstützen, zum Beispiel durch das Anlegen und Betreuen von Fehlerberichten, sogenannten Issues. Mit einem kostenlosen Account bei Gitlab könnt ihr solche Issues für das Foodsharing-Projekt anlegen. Es gibt verschiedene Templates, mit denen diese Meldungen strukturiert werden können (und sollten). Wichtig: Die Issues sind idealerweise so ausführlich, dass auch Menschen sie nachvollziehen können, die (noch) keine aktiven Foodsaver sind. Hilfreich sind Screenshots und Schritt-für-Schritt-Anleitungen, mit denen sich Fehler reproduzieren lassen.

Zu guter Letzt: Herzlichen Dank, dass Ihr mitmacht! Sowohl IT als auch alle Nutzer*innen der Seite sind Euch sehr dankbar (auch wenn Letztere manchmal nicht wissen, wieso).

PHP

This page describes broadly how PHP is used in our project:

For more specific explanations of the types of classes we use, make sure to check PHP Structure!

Main entry point

The main entry point for the web is /index.php. That means that /index.php gets called whenever a https://foodsharing.de-request gets sent to the website.

Another entry point is xhr.php, which is used for routes starting with https://foodsharing.de/xhr.php. These are used for our legacy API (see Xhr). The same applies for xhrapp.php.

These three entry points are now just standard Symfony entry points. The relevant (custom) code has been moved to src/Entrypoint, where three respective controllers do the actual work.

The fourth entry point is restApi.php, which will be used whenever a URL starting with https://foodsharing.de/api is requested. This is the route to our modern API following REST principles (see REST-API). It is also a standard Symfony entry point.

Dependencies and Dependency Injection

Autoloading

(Almost) no code comes without dependencies on other PHP classes.

In order to use a class defined in another file, it needs to get imported at the beginning of your file using use, following a namespace.

The namespace, starting with Foodsharing gets interpreted by the composer autoloader. This is configured in /composer.json under the key "autoload", where Foodsharing gets mapped to /src. Other namespaces than Foodsharing link to external libraries, which have their source code somewhere inside the /vendor directory.

The composer autoloader loads files corresponding to conventions specified by the PSR-4 standard. That means that every subnamespace following the Foodsharing namespace gets interpreted as subdirectory under /src.

use is not necessary for using classes in the same namespace (roughly: same directory).

Loading a depencency via require is just used in files like index.php, which are not in the /src directory and therefore can't use autoloading. require basically executes the PHP file unter the given file name, so you can use the class declaration defined in that file. Don't use require if you can use autoloading.

Services

A service class is a class whose main purpose is not representing an object structure, but providing functionality. Service classes are used to structure operations using object oriented design patterns.

What exactly a service is and what not, is not well defined (see Blog Post Services in Domain-Driven Design), but there are some characteristics that are typical for services

  • only one instance is created per requests and then shared by all classes
  • services depend on other services and the non-service objects (like DTOs) they operate on
  • non-service objects don't depend on services

As we don't have any entity classes, except for the DTO classes, nearly all of our classes are services.

Because service classes only need to be instantiated once, we don't use the new statement to create instances. Instead, we use the Dependency Injection pattern. This enables us to share service instances throughout the application.

Automatic service injection

Responsible for creating and injecting the instances is the Symfony Dependency Injection component.

If we want Symfony to inject a dependency into our service, all we need to do is mentioning the class in our constructor (__construct), e.g.

class ActivityXhr extends Control {
  private $mailboxGateway; /* attributes (member variables) */
  public function __construct(ActivityModel $model, MailboxGateway $mailboxGateway)
    { /* using arguments for setting attributes */ }
... }

Dependency injection (Symfony) then makes sure that every service we request is created and injected when our service is instanciated.

This works because we only need at most one object of every service class. This is because, as already mentioned, service classes are not about representing objects using instances, but about the functionality they provide.

Symfony config is in /config/services.yml. This configuration makes sure that services get created automatically. See the Symfony docs for further reference.

Syntax

Here we collect the parts of the PHP language that we use in this project. As I write this I already got some experiences in other languages and therefore only include short explanations of surprising syntax.

  • Variables: all occurences of variables are starting with a Dollar ($) symbol:
$out = '<ul class="linklist baskets">';
  • = is an assignment
  • 'string' and "string" are strings. Variables inside double-quoted string literals get replaced by their values.
  • . is the concatenation operator for strings (in other languages +)
  • .= adds the string to the right to the variable on the left
  • $var->member refers to a member variable or method of an object (in other languages .)
  • ClassName::member refers to a (static) member variable or function of a class
  • ... is used for packing and unpacking arrays in argument lists (packing in function definition argument list, unpacking in function use argument list). Longer explanation on stackoverflow

Conventions

  • Do not use global variables (those indicated by the keyword global, can be found in legacy code)
  • Indentation: we use one tabstop per indentation level
  • Variables in strings: we do not use variables in strings but concatenate:
$list = 'bla'
$list .= $list . 'some text' . $someVariable . 'end text'
  • When (re)writing php code add types in arguments and return types of functions. If you work with existing code it is not always trivial and is therefore not enforced. In general:
    • as a parameter, something should be either the type (e.g. string, int) OR null if okay to not be set. ?string $blub = null works as a parameter definition to allow that.
    • as a return type, something should always ever be one type, if possible. Throw an exception otherwise. Especially an empty array [] is fine to say that there is no data when otherwise data would have been returned as an array.

Code structure

For the structure of the PHP code, see the following sections. Different kinds of classes and concepts are explained there:

If you are looking for a general description of our PHP setup, please check PHP!

Modules

A lot of code is sorted into modules in the /src/Modules directory. This is a sorting by topic: each module contains files for one topic. That can be a gateway, a controller, an (old) view, javascript, css, (old) XHR, (old) models.

The Rest api controllers do not go into their respective module directory but into the /src/Controller directory. This does not have a good reason but it's the way it is now.

Deprecated module structure

Since legacy code is still widespread through the repository it is important to understand it, too.

The (php) code is roughly structured with Model - View - Controller.

The communication with the database is found in Model classes. For example we can find sql-commands to manipulate a foodsaver in /src/Modules/Foodsaver/FoodsaverModel.php. Those are executed with the functions inherited from the Db class (see use Foodsharing\Lib\Db\Db;, for example $this->q(...) where q stands for query.

Newer module structure

Instead of Model classes, that hold both, data query logic and domain logic, we move towards splitting these up into Gateway classes and Transaction classes.

For a general description what „domain logic“ is, see section Transactions.

Note that all of the following guidelines have a lot of exceptions in the existing code. Nevertheless try to heed the following guidelines in code you write and refactor.

Gateways

Our concept of Gateway classes follows the Table Data Gateway pattern.

One main difference to Models is that a Gateway doesn't contain the actual model of an entity, as the overall domain logic is put into Transactions while the structure lives in Data Transfer Objects.

The purpose of a Gateway is to provide functionality to query instances of a certain entity type from the database. If you are familiar with ORM based architectures, you might compare the Gateway's responsibility to the one of a Repository.

As methods to be found on a Gateway class have the job to perform queries, they should be named in a way that portrays this. They should not pretend to perform domain-related business logic. A method name suitable for a Gateway class would be selectResponsibleFoodsavers() or insertFetcher(). A method not suitable would be addFetcher(), as this implies that the method took care of the whole transaction of adding a fetcher to a store pickup. In particular permission checks are not to be found in Gateways.

Another difference to models regarding the implementation of SQL queries is that the functions to communicate with the database are not directly in the Gateway class by inheritance but encapsulated in the attribute db ($this->db-><functioncall>) of class Database defined in /src/Modules/Core/Database.php.

Gateways inherit from BaseGateway (/src/Modules/Core/BaseGateway.php), which provides them with the $db attribute.

If possible, use semantic methods like $db->fetch() or $db->insert() to build your queries. Often, requesting information from the database uses sql calls via the functions at the end of the Database class, like $db->execute() - don't use these unless you can't build your query otherwise.

All of those functions are well-documented in /src/Modules/Core/Database.php.

Individual gateway functionality

Please refer to our list of Gateway classes below if you're looking for specific functionality:

ActivityGateway.php

ApplicationGateway.php
 - handles workgroup applications

BasketGateway.php
 - adding, editing, requesting food baskets, managing their availability
 - querying food basket data, listing new baskets nearby

BellGateway.php

BlogGateway.php

BuddyGateway.php
 - add someone as buddy, respond to that, get list of buddies

BusinessCardGateway.php

ContentGateway.php
 - basic CMS functionality: display, create, edit, delete pages with fixed contentId
 - some content is used in page templates and just a sentence, other content consists of full pages

DashboardGateway.php
 - just basic user info
 - we have an issue to investigate if `countStoresWithoutDistrict` & `setbezirkids` are still needed

EmailGateway.php

EventGateway.php
 - add or edit events, manage invitations (target audience) and their RSVP
 - get list of events in a region, query people who are interested
 - also has some weird event location storage

FoodsaverGateway.php
 - almost 1000 lines of code :)

FoodSharePointGateway.php

GroupGateway.php
 - groups handle functionality that is shared between both regions and workgroups
 - currently only has a few basic helper, and the complex hull closure computation

GroupFunctionGateway.php
 - group functions are currently only used for workgroups, but can attach to both regions and workgroups
 - includes adding and removing the special function groups, and querying whether they exist
 - there are 3 available functions right now; see `Modules/Core/DBConstants/Region/WorkgroupFunction.php`

LegalGateway.php

LoginGateway.php

LookupGateway.php

MailboxGateway.php

MailsGateway.php

MaintenanceGateway.php
 - data needed for cleanup and bookkeeping executed each night (see `MaintenanceControl.php`)

MapGateway.php

MessageGateway.php

MigrateGateway.php

PassportGeneratorGateway.php

ProfileGateway.php

PushNotificationGateway.php

QuizGateway.php

QuizSessionGateway.php

ForumGateway.php

ForumFollowerGateway.php

RegionGateway.php

WorkGroupGateway.php

ReportGateway.php
 - currently unused

SearchGateway.php

SettingsGateway.php

StatisticsGateway.php

StatsGateway.php

StoreGateway.php

TeamGateway.php

UploadsGateway.php

VotingGateway.php

WallPostGateway.php

Transactions

All modules have certain business rules/domain logic to follow when their data is modified. After all, there are always certain operations that have to be executed together to ensure that the data keeps being consistent according to the the rules that apply to them in reality. We implement these transactions of operations executed together as methods on Transaction classes.

For example, when someone wants to join a store pickup, it's not enough to just insert this information into the database. We also have to be check if the user has the rights to join without a confirmation, and if not, we have to make sure that the store owner gets notified that they should confirm or deny it.

This is why joining a pickup is implemented in the joinPickup() method on the corresponding Transaction class. All controllers should use this transaction if they want to make a user join a pickup, because only if all steps of the transaction are executed, the pickup joining is complete.

What should not be part of a transaction class:

  • knowledge of the underlying database (should still work with a gateway reading from punched cards)
  • knowledge of request types (e.g. should be callable from a desktop application or some different internet protocol). Therefore transaction classes do not raise HTTPException or choose HTTP response codes or the json representation of responses
  • the session - but at this point we are not strict, so far transaction classes use information of the session

Permissions

Permission classes are used to organize what actions are allowed for which user. They are a special type of transaction class.

Data Transfer Objects

Currently, domain objects are often represented differently: Some methods receive and return them as associative arrays, some receive them as a very long list of parameters. If arrays are used, it's often unclear which format the output has and which format the input is expected to have. Parameter lists on the other hand can get very long, and if parameters are documented, the documentation for one domain object is spread around the code.

For further structuring Data Transfer Objects (DTO) can be used. An example can be found in the Bell module, introduced in the merge request !1457.

DTOs help with clearing up which parameters are expected when and what types they have. DTO classes have public properties and don't encapsulate logic or functionality. Only logic to create DTOs or convert them from other representations shall be implemented on the DTO classes as static methods.

As objects are often represented differently, as only parts of them are needed, most domain objects have multiple DTO representations. That's why we put them in a DTO directory inside of the corresponding module directory. Usually, there is one main or "complete" representation, that includes all aspects of the domain object that can be found in its database table. This one is just named like the domain object itself (e. g. Bell). All other partial represantations can be named according to their purpose or the place they are used (e. g. BellForList).

Controllers

Controllers handle requests. They define how to extract relevant information from the request, check permissions by calling the correct Permission and calling the suitable transaction. They define which HTTP response including the response code is sent back and the conversion of internal data to json (return $this->handleView(...)).

Since the business logic („What is part of an event (= transaction)?“) is in the transaction classes, a controller method usually just calls one actual transaction method (apart from permission checks). It can read necessary information from the session to give those as arguments to the transaction class.

We have:

  • REST controllers with the name <submodule>RestController.php
  • (legacy) XHR controllers with the name <module>Xhr.php
  • (legacy) HTML controllers with the name <module>Control.php

Services

Currently, in our source code, some code that assists controllers can be found in classes named "Service": /src/Services. Some of these classes are Transaction classes that need to be renamed, and some of them are utility classes. /src/Services/SanitizerService.php is the best example for that.

Some code in the services should rather go into the modules if they belong to a specific module.

Also see the section Services for a broader use of this term.

Helpers

The content of /src/Helpers is a collection of code that somehow had no better place. The word Helper does not say anything. Rather try to find a suitable place for it.

Routing

For the new REST API, Routing happens completely through Symfony, using @Route annotations in the respective controllers. (also, see config/routes/api.yaml)

Everything else (the website, xhr and xhrapp) uses GET parameters to determine the controller and action to call. See src/Entrypoint for the implementations, and src/Lib/Routing.php for how the page= GET parameter corresponds with controller class names.

Last, there are some special routes that consist of:

  • a location and a try_files directive in the web server's config For the development environment, you can find them here: https://gitlab.com/foodsharing-dev/images/-/blob/master/web/foodsharing.conf
  • Symfony routes to make symfony call the correct entrypoint for all possible URI forms in config/routes/special.yaml.

Deprecated module structure

Since legacy code is still widespread through the repository it is important to understand it, too.

The (php) code is roughly structured with Model - View - Controller.

The communication with the database is found in Model classes. For example we can find sql-commands to manipulate a foodsaver in /src/Modules/Foodsaver/FoodsaverModel.php. Those are executed with the functions inherited from the Db class (see use Foodsharing\Lib\Db\Db;, for example $this->q(...) where q stands for query.

Newer module structure

Instead of Model classes, that hold both, data query logic and domain logic, we move towards splitting these up into Gateway classes and Transaction classes.

For a general description what „domain logic“ is, see section Transactions.

Note that all of the following guidelines have a lot of exceptions in the existing code. Nevertheless try to heed the following guidelines in code you write and refactor.

Libraries

Please explain the general content of /src/Lib after understanding it.

Refactoring

We want to continue refactoring the codebase. This will lead to code which is more modular, easier to maintain and clearer. A lot is explained in the book "Modernizing Legacy Applications In PHP" (mentioned here in more detail)

There is an overview issue with a checklist.

If you have an idea for a new refactoring concept, please think, before implementing, if this would increase the work for others :).

Front-end

Currently, we use three different ways for the front-end code.

The oldest one is in the View files, in which php functions build the HTML and JavaScript code of the website.

If you refactor something or want to make something new, please use vue.js

There are also twig templates used.

It is not necessary to refactor these solely for the purpose of refactoring, but if you try to fix something in an old file, it would be great to refactor it to vue.js then. If you are making a new front-end page, you should definitivly use vue.js.

Network

Request handling is explained in the here and more about Rest API endpoints can be found here.

Back-End

Some explanation can be found here.

Helper classes could be used to put some functions unorganized there. Please think about if there might not be a better class to put it. Some parts might be better in Service classes. (If in doubt, please ask the team.)

Database Access

Currently there are SQL statements in *Model classes, in XhrMethods and in *Gateway classes. More information can be found here.

Issue #9 tracks the refactor procress of moving everything to *Gateway classes.

Internationalization

We want the code to be free from German-specific texts to enable non-German speakers to use the site and to make it easier for groups in other countries to use the code. Unfortunately, we are not quite there yet, but a lot of progress has been made and the remaining hardcoded strings are indicative of obvious legacy code.

We use the Symfony TranslatorInterface to replace translation keys (given in code) with their translated values. Translations are read from files in YAML format: for German that would be /translations/messages.de.yml. Aside from those German strings, there are four mail ways in which fixed text on the website can be given in the source code with our translation mechanisms:

  • In PHP frontend/view code Classes derived from Control and View already contain the TranslatorInterface, which is accessible as $this->translator, and its trans() function that resolves translation keys.
    Other helpers are including it like this:
use Symfony\Contracts\Translation\TranslatorInterface;
// instantiate or inject (usually autowired)
$this->translator->trans('fsp.acceptName', ['{name}' => $foodSharePoint['name']])
  • From JavaScript code In client/src/i18n.js we build a basic replacement function that imports translation resources, replaces variables if needed, and returns the translated value if one exists. Fallbacks are implemented for English and German, which are currently the only languages for which we bundle translations.
    To use this function elsewhere, import the module like so:
import i18n from '@/i18n'
pulseInfo(i18n('basket.not_active'))
  • As part of Vue templates In client/src/vue.js we define the $i18n prototype and attach it to all Vue templates. This is the preferred way of frontend translation. If you're translating component parameters, make sure to :bind them accordingly: :title="$i18n('button.clear_filter')"
  • As part of Twig templates (Legacy) The trans filter allows passing translation keys to the translation engine from Twig files. Examples:
{% raw %}{% embed 'components/field.twig' with {'title': 'events.bread'|trans} %}{% endraw %}
{% raw %}<h4>{{ 'dashboard.push.title'|trans }}</h4>{% endraw %}

Do not create more of these unless you really know what you're doing! You will need to be very careful with the syntax for replacing variables ({% raw %}|trans({'%name%': name}{% endraw %})).

Helper scripts

There are a number of helper scripts available. Most of them obey the FS_INT env var. Default is dev, you can also set it to test.

Script overview

ScriptPurpose
./scripts/ci.test
./scripts/cleanRemove anything added by start/test commands
./scripts/codecept
./scripts/composerRun php composer
./scripts/deploy.notifyslack.sh
./scripts/deploy.sh
./scripts/devRun webpack dev server for doing js dev (obsolete, included in ./scripts/start)
./scripts/docker-composeDocker-compose with the correct options set for the environment
./scripts/dropdbDrop the database
./scripts/fixruns all fixing - code stuff (php)
./scripts/fix-codestyle-localfix php code style, see php Code style
./scripts/generate-revision.sh
./scripts/inc.shdefines functions needed in other scripts
./scripts/initdbCreate the database and run migrations
./scripts/lintruns all lintings scripts lint-...
./scripts/lint-jslints javascript and vue files: prints errors etc.
./scripts/lint-phplints php files: prints errors etc.
./scripts/mkdirsCreate directories that need to be present (called by other scripts)
./scripts/mysqlRun mysql command in correct context: ./scripts/mysql foodsharing "select * from fs_foodsaver"
./scripts/mysqldumpRun mysqldump command in correct context
./scripts/php-cs-fixer
./scripts/php-cs-fixer.ci.sh
./scripts/rebuild-all
./scripts/rebuild-container
./scripts/rebuild-test-data
./scripts/rmShut down and clean up all containers
./scripts/run
./scripts/seedRun seed scripts in scripts/seeds/*.sql
./scripts/startStart everything, initializing anything if needed, see Setting things up
./scripts/stopStop everything, but leave it configured see Setting things up
./scripts/testRun tests
./scripts/test-chatRun test for the chat
./scripts/test-jsRun test for the client (JS)
./scripts/test-rerunRun tests without recreating db (faster that test)
./scripts/watch-assetsBuilds the static assets on change

Nightly maintenance

Using the docker-compose you can run various php-scripts, e.g.

./scripts/docker-compose run --rm --no-deps app php -f run.php Stats foodsaver
./scripts/docker-compose run --rm --no-deps app php -f run.php Stats betriebe
./scripts/docker-compose run --rm --no-deps app php -f run.php Stats bezirke

This runs the statistics scripts that are run nightly on the production server. This can be necessary to test code concerning statistics since they are usually never run locally. --rm removes the containers afterwards, --no-deps lets docker not worry about any dependendent containers. This is often useful since they are often running already.

HTTP Request

The traditional loading of a page is a http request, e.g. calling the main address https://foodsharing.de calls /src/Entrypoint/IndexController.php which uses other php files to answer the request. The php builds html, css and javascript and sends them to the client.

Other ways to interact with the foodsharing platform are:

XHR

XHR (XMLHttpRequest) is used throughout the project for historic reasons, but should be replaced with modern API endpoints where possible. So do not implement new features with XHR! The following is just documentation to understand what exists :)

We used XHR for information transferred from the server to the client which is not a complete new page but javascript-initiated. For example, the Update-Übersicht on the Dashboard was loaded by an XHR that gets a json file with the information of the updates. The javascript was found in /client/src/activity.js, and it called XHR endpoints like http://foodsharing.de/xhrapp.php?app=basket&m=infobar.

This requests an answer by /src/Entrypoint/XhrAppController.php which in turn calls the correct php file based on the options that are given after the ? in the url. For example, the activity.js requests were answered by /src/Modules/Activity/ActivityXhr.php. In this example, the database was queried for information via ActivityModel.php which in turn used the /src/Modules/Activity/ActivityGateway.php.

There are a two mostly identical XHR endpoints - /xhr.php and /xhrapp.php. Nowadays, those are handled by XhrController.php and XhrAppController.php respectively.

XHR-request answers contain a status and data and ? and always sends the HTTP status 200. So errors are not recognizable by the HTTP status, but by a custom status in the returned json response.

REST API

The more modern way to build our api is a REST api by FOS (friends of symfony). The documentation of the REST api endpoints is located at the definition of the endpoints and can be nicely viewed on (https://beta.foodsharing.de/api/doc/).

In the documentation you can read how to properly include the documentation. A good example can be found in /src/Controller/ForumRestController.php.

In the Code quality page we have some notes on how to define the REST API Endpoints.

The javascript code that sends REST API requests is found under /client/src/api and is used by other javascript by import.

All php classes working with REST requests are found in /src/Modules/Controllers/<..>RestController.php. This is configured in /config/routes/routing.yml. There it is also configured, that calls to /api/ are interpreted by the REST api, e.g.

https://foodsharing.de/api/conversations/<conversationid>

This is being called when you click on a conversation on the „Alle Nachrichten“ page.

REST is configured via annotations in comments in functions.

  • @Rest\Get("subsite") specifies the address to access to start this Action: `https://foodsharing.de/api/subsite"
  • @Rest\QueryParam(name="optionname") specifies which options can be used. These are found behind the ? in the url: http://foodsharing.de/api/conversations/687484?messagesLimit=1 only sends one message.
  • Both Get and QueryParam can enforce limitations on the sent data with requirement="<some regular expression>".
  • @SWG\Parameter, @SWG\Response, ... create the documentation (see above)

Functions need to have special names for symfony to use them: the end with Action. They start with a permission check, throw a HttpException(401) if the action is not permitted. Then they somehow react to the request, usually with a Database query via the appropriate Model or Gateway classes.

During running php, the comments get translated to convoluted php code. REST also takes care of the translation from php data structures to json. This json contains data. Errors use the error codes of http-requests.

While reading and writing code a (basic) manual and an annotation overview will help.

nodejs for messages

The chats have a separate way of communication between client and server. For all other pages the initiative starts at the client, the client sends a request to the (php) server and gets an answer. Each request uses one connection that is closed afterwards. This is not useful for the chats since the server knows that there are new messages and has to tell the client.

For this case there exists a separate nodejs server (on the same machine as the php server but separate). This holds an open connection to each user that has foodsharing.de open on their device. Each time a message arrives at the php server, it sends this information to the nodejs server via a websocket

which uses the connection to the client to send the message. Note that there can be several connections to each session, of which there can be several for each user. nodejs sends the message to all connections of all addressed users.

The code for the nodejs server is found in /chat/src/index.ts and other files in /chat/src chat/socket.io -> nodejs server, in chat/src/index.ts. There is documentation for all used parts in /chat/node_modules/<modulename>/Readme.md. All nodejs-documentation is found on their webpage.

XHR

XHR (XMLHttpRequest) is used throughout the project for historic reasons, but should be replaced with modern API endpoints where possible. So do not implement new features with XHR! The following is just documentation to understand what exists :)

We used XHR for information transferred from the server to the client which is not a complete new page but javascript-initiated. For example, the Update-Übersicht on the Dashboard was loaded by an XHR that gets a json file with the information of the updates. The javascript was found in /client/src/activity.js, and it called XHR endpoints like http://foodsharing.de/xhrapp.php?app=basket&m=infobar.

This requests an answer by /src/Entrypoint/XhrAppController.php which in turn calls the correct php file based on the options that are given after the ? in the url. For example, the activity.js requests were answered by /src/Modules/Activity/ActivityXhr.php. In this example, the database was queried for information via ActivityModel.php which in turn used the /src/Modules/Activity/ActivityGateway.php.

There are a two mostly identical XHR endpoints - /xhr.php and /xhrapp.php. Nowadays, those are handled by XhrController.php and XhrAppController.php respectively.

XHR-request answers contain a status and data and ? and always sends the HTTP status 200. So errors are not recognizable by the HTTP status, but by a custom status in the returned json response.

REST API

The more modern way to build our api is a REST api by FOS (friends of symfony). The documentation of the REST api endpoints is located at the definition of the endpoints and can be nicely viewed on (https://beta.foodsharing.de/api/doc/).

In the documentation you can read how to properly include the documentation. A good example can be found in /src/Controller/ForumRestController.php.

In the Code quality page we have some notes on how to define the REST API Endpoints.

The javascript code that sends REST API requests is found under /client/src/api and is used by other javascript by import.

All php classes working with REST requests are found in /src/Modules/Controllers/<..>RestController.php. This is configured in /config/routes/routing.yml. There it is also configured, that calls to /api/ are interpreted by the REST api, e.g.

https://foodsharing.de/api/conversations/<conversationid>

This is being called when you click on a conversation on the „Alle Nachrichten“ page.

REST is configured via annotations in comments in functions.

  • @Rest\Get("subsite") specifies the address to access to start this Action: `https://foodsharing.de/api/subsite"
  • @Rest\QueryParam(name="optionname") specifies which options can be used. These are found behind the ? in the url: http://foodsharing.de/api/conversations/687484?messagesLimit=1 only sends one message.
  • Both Get and QueryParam can enforce limitations on the sent data with requirement="<some regular expression>".
  • @SWG\Parameter, @SWG\Response, ... create the documentation (see above)

Functions need to have special names for symfony to use them: the end with Action. They start with a permission check, throw a HttpException(401) if the action is not permitted. Then they somehow react to the request, usually with a Database query via the appropriate Model or Gateway classes.

During running php, the comments get translated to convoluted php code. REST also takes care of the translation from php data structures to json. This json contains data. Errors use the error codes of http-requests.

While reading and writing code a (basic) manual and an annotation overview will help.

nodejs for messages

The chats have a separate way of communication between client and server. For all other pages the initiative starts at the client, the client sends a request to the (php) server and gets an answer. Each request uses one connection that is closed afterwards. This is not useful for the chats since the server knows that there are new messages and has to tell the client.

For this case there exists a separate nodejs server (on the same machine as the php server but separate). This holds an open connection to each user that has foodsharing.de open on their device. Each time a message arrives at the php server, it sends this information to the nodejs server via a websocket

which uses the connection to the client to send the message. Note that there can be several connections to each session, of which there can be several for each user. nodejs sends the message to all connections of all addressed users.

The code for the nodejs server is found in /chat/src/index.ts and other files in /chat/src chat/socket.io -> nodejs server, in chat/src/index.ts. There is documentation for all used parts in /chat/node_modules/<modulename>/Readme.md. All nodejs-documentation is found on their webpage.

javascript

Javascript is code that is run in the browser of the client.

For every page (module), the main javascript file is found under /src/Modules/<modulename>/...js. This is automatically sent to the client. All other javascript (should) be found under /client.

imports

Javascript has different ways of using (importing) code from other files. We use npm, the standard package manager for javascript.

All used third-party-packages are listed under /client/package.json. For using a package, we use the import command at beginnings of files. During build of the webpage the imports are resolved (see webpack) and only what is necessary is sent to the user.

To enable small bits of javacode somewhere (inline), there is /client/src/globals.js. There a bunch of functions are made avaiable globally. This is imported in the main module js-files via import '@/core' and import '@/globals'.

webpack

The javascript files are not directly sent to the client but preprocessed by webpack. Webpack is configured via /client/webpack.base.js. For example this includes the alias that translate @ in imports into /client/src.

vue.js

The modern way of designing front end pages is vue.js. We try to use more of it do get more order into javascript files. The big advantage is, that html, javascript and css are clearly separated. One example is /client/src/components/Topbar/Login.vue. Before diving into happy vue.js-hacking you probably should read a bit in the vue documentation.

Icons

For including icons in the website we do not create a lot of them ourself but use Font Awesome. In order to use an icon, use the code proposed by Font Awesome. For example it looks like

<i class="fas fa-user" />

in html code like .vue-js files. That explains where this unexplained class="fas/r/b code comes from.

MySQL database

The MySQL database saves data on a hard drive. It holds all long-term information. Querying information is done via sql in Model.php and Gateway.php classes. More detailed information about queries can be found in the php reference.

Related issue: Extract SQL Statements To Gateways.

Database tables and columns

As described in Set up and Database access we can view the dev database in a browser via phpmyadmin. Many table and column names should be self-explanatory. Many are not unfortunately. (If you create new tables, make sure a comment is not necessary!) That's why here we start a list with explanations to the tables and columns. Please add to this when it was not obvious to you what a table/column was representing and you figured it out. If you found this information well described in some file (e.g. some Gateway-php-file), please just link this location to avoid information duplication.

Some information can also be inferred by the list of limitations and problems listed in /database_fixup.md in the top level directory.

Theoretically there is also the possibility to add a comment for the column in the database, e.g. for team status in fs_betrieb. It hasn't been established to use those and adding them later is not as easy as adding the information here since it would require Database migration.

The tables also specify indices. That are columns that are often searched. Read the php manual for more detailed explanation.

Redis database

The Redis database saves data in memory. It holds all short-term information and caches some of the information gotten from the MySQL database. Information in Redis include session IDs (Who is loged in?), and email queues.

Database Migration

We are using phinx for database migrations. If your code change requires customization of the database, migration templates can be created with ./scripts/docker-compose exec app vendor/bin/phinx create MyNewMigration

Workaround (Here we should look for a better solution)

Run the command sudo chmod 775 migrations/yourfile before your first edit.

You can test the migration with ./scripts/docker-compose exec app vendor/bin/phinx migrate --dry-run (which "runs" all not yet applied migrations which in that case should be just your new one).

For examples you can have a look at migrations/ folder and https://book.cakephp.org/phinx/0/en/migrations.html.

Database tables and columns

Since tables are sorted alphabetically in phpmyadmin, sort them here alphabetically:

fs_abholer

  • decribes who fetches when where and if confirmed
  • confirmed 1, not yet confirmed 0

fs_abholzeiten

Contains information about regurlary reoccuring pickup slots. Additional single pickup slots are stored in #TODO: find table. The columns to be described are

columndescriptionpossible values
dowday of week1 = Monday, 0 = Sunday
timewhen on the day the pickup istime
fetchernumber of slots>= 0, >= 1 enforced by frontend

fs_bezirk

describes the regions

  • type: description in src\Modules\Core\DBConstants\Region\Type

fs_foodsaver

  • deleted_at: deletion day of account, if NULL, account is active

fs_foodsaver_has_bezirk

describes which foodsaver is in which workgroups and regions

anything with theme

theme are threads in the forum.

Tests

We use Codeception for testing, especially testing the php code.

Useful commands and common pitfalls

Useful commands for testing and common pitfalls.

CommandActionPitfall
amOnPageChanges URL, loads page, waits for body visibleDo not use to assert being on a URL
amOnSubdomainChanges internal URL stateDoes not load a page
amOnUrlChanges internal URL stateDoes not load a page
clickFires JavaScript click eventDoes not wait for anything to happen afterwards
seeCurrentUrlEqualsChecks on which URL the browser is (e.g. after a redirect)
submitFormFills form details and submits it via click on the submit buttonDoes not wait for anything to happen afterwards
waitForElementWaits until a specific element is available in the DOM
waitForPageBodyWaits until the page body is visible (e.g. after click is expected to load a new page)

Releasing

We want to release the beta version of the foodsharing website to production every two months. For one to two weeks, we stop merging features and focus on testing and bug fixing. If there are features not to be used for the next release, they are disabled. If there are still release blockers, we will talk about them in a call and decide what to do.

As soon as everything is ready, the master branch can be merged into the production branch. People with rights for this can be found in the gitlab-Wiki.

For organization, we use gitlab milestones. Their titles consist of the month and the year of the release and a fruit. If you are working on something which belongs to the next release, feel free to add it to the milestone. Please don't recycle milestones, but always use a new one for each release.

Update our software dependencies

Slack

Every Sunday a schedules pipeline "send outdated dependency report to slack" is started and the result can be read in the channel #fs-outdated.

Update from backend using Composer

Manually check the version

./scripts/composer outdated -D Restricts the list of packages to your direct dependencies

The color coding is as such:

green (=): Dependency is in the latest version and is up to date.
yellow (~): Dependency has a new version available that includes backwards compatibility breaks according to semver, so upgrade when you can but it may involve work.
red (!): Dependency has a new version that is semver-compatible and you should upgrade it.

https://getcomposer.org/doc/03-cli.md#outdated

Structure and explanation of version numbers

  • 0.x.x is a beta version. Here every change can contain ``Breaking Changes''.

2.3.5
│ │ │
│ │ └───────── Patch (contains mostly bug fixes)
│ └─────────── Minor version (mostly functional extension)
└───────────── Major version (mostly significant change)

Rules

  • Don't mix dev dependencies with dependencies in a commit
  • ~ instead of ^ to have similar systems between server and dev computer and to avoid big unwanted changes during yarn update
  • only run yarn update if there are only outdated packages with explicit version information
  • If you don't know what belongs together, then update only one package per commit.
  • Major updates are best done in your own MR with changelog entry.

Update

  • Change of version number in composer.json

./scripts/composer update PACKAGENAME* --with-dependencies

Update from frontend using yarn

Manually check the version

  • ./scripts/docker-compose run --rm client sh
  • yarn outdated

Manually check for security vulnerabilities

  • ./scripts/docker-compose run --rm client sh
  • yarn audit

Update client

  • Change the version number in client/package.json
  • ./scripts/docker-compose run --rm client sh
  • yarn PACKAGENAME

Update chat

  • Change the version number in chat/package.json
  • ./scripts/docker-compose run --rm chat sh
  • yarn PACKAGENAME

Update deployer

  • Change the version number in deployer/package.json
  • ./scripts/composer update -d deployer

Tests after every single update

  • ./scripts/lint for javascript and php

API

Application interface

DOM

Document Object Model

DPP

Data Protection Policy

DVCS

Distributed Version Control System

FOS

Friends of symfony. We use the FOSRestBundle.

json

Javascript object notation. Format for XHR and REST data transfer.

MR

Merge Request

MySQL

The database system we are using. Relational database

npm

Node.js Package Manager (package manager for javascript)

php

Hypertext Preprocessor. Server side-scripting language

PR

Pull Request

REST

REpresentational State Transfer. We use the FOSRestBundle

SQL

Structured Query Language. Language to ask a database for data

ToU

Terms of Use

VCS

Version Control System

WIP

Work In Progress (prefix for e.g. merge requests)

About the devdocs

Feel free to provide feedback or ask questions at the #foodsharing-dev Slack channel at any time.

General information

The developer documentation (devdocs) contains general information about the foodsharing website project, step-by-step instructions, and references.

Target groups of the devdocs

The devdocs should offer everything newcomers need to start participating in the foodsharing website project.

The devdocs should also be of help to anyone that got stuck while working on the foodsharing website project and is in need of help.

Contributing to the devdocs

Anyone can contribute to the devdocs. The git project directory is called docs.

The devdocs are based on the following principles:

  • Information should be correct and current
  • Information should be consistent (language, formatting,...)
  • Information should be concise (but some repetition is necessary)
  • Information should be complete (probably an infeasible ideal)

But don't worry too much about the last three principles.

What in, what out?

How do I decide if a specific information should be in the devdocs? Here are some principles:

  • Is the information specific to foodsharing? -> Yes
  • Were you surprised about it? -> Yes
  • Is the information general enough to be explained in a foodsharing-independent tutorial or documentation? -> No, include a link
  • Is the information specificly about one piece of code and only interesting during reading this code (and not for finding this piece of code)? -> No, explain the code with comments in the code.

Markdown

The devdocs are written in Markdown (md). Additionally we use the plugin richquotes.

Setting things up

The devdocs are built at every change on the master branch and published here. To see your changes, you can build the devdocs locally. You need to have nodejs and yarn installed on your system. On Windows: After every install you might have to close and reopen the powershell so yarn is recognized as a command. Then go to the main directory / and run the following

# this makes sure you will not commit the created files:
echo "package.json\nyarn.lock" >> ../.git/info/exclude
yarn add gitbook-cli
# only the following is necessary for later runs:
yarn run gitbook install docs && yarn run gitbook serve docs -- port 4001

This makes the current devdocs available via localhost:4001 in your browser.

It is updated at every change of the files. You do not need to send the gitbook serve docs --port 4001 again when you save a changed file

It would be nice to have a docker setup (to avoid the local yarn dependency). Please document it here if you set it up.

The gitlab ci is not triggered if you push with the option git push -o ci.skip. This is useful if you work on the devdocs since they are only built on master anyway.

Check for broken links

You can check for broken links with the node tool broken-link-checker. You can install it with yarn: (and ignore created files)

yarn add broken-link-checker
echo "package.json\nyarn.lock" >> ../.git/info/exclude

and use it while gitbook is serving it:

yarn run blc --recursive --exclude-external http://localhost:4001

(Of course you can remove --exclude-external but I got a lot of false negatives. See for yourself.)

GitBook

The devdocs are built with GitBook.

Contributors

💻 = Code  📝 = Documentation  🎨 = Design  🐜 = Bug reports  💡 = Ideas, Planning, & Feedback  🔐 = Security  🔩 = Infrastructure (Hosting, Build-Tools, etc)  👀 = Reviewed Pull Requests  💬 = Answering Questions  🏢 = Board member

💻
Emilia Paz
💻
Jannis R
💻
Michael Aydinbas
💻 📝
Valentin
💻
Valentin
💻
Theo
🐜 💻 🎨
Michael Paul Killian
💻
Basti A.
🐜 💻 📝
Christian
💻 📝
Felix
🐜 💡
Boji
🐜 💡 🔐
irgendwer
💻
Raphael
💻
Timo Hartmann
💻 📝 🔩 👀
Nick Sellen
🏢 🐜 💻 💡 💬
fs_k
💻
D0nPiano
💻
Nigel Green
🏢 🐜 💻 💡 💬
Johannes
💻
Adrian Heine
💻 📝
Thomas Pfeiffer
🐜 💻 💡 💬 👀
Peter Tönnies
💻
Jano
💻 👀
Tilmann Becker
💻 🔩 👀
Matthias Larisch
💻 👀 🔐
chandi
💻 💡
djahnie
📝
Jana Aydinbas
🏢 💡
Manuel Wiemann
🎨 💡
mel
💻
Peter Reutlingen