Inside-Informationen über Pokefans: was bei uns so unter der Haube läuft. Kurioses, Interessantes oder auch einfach nur Einblicke in das, was wir so den lieben langen Tag machen.

Zum Inhalt | Zum Menü | Zur Suche

Donnerstag, 14. Juli 2016

Ladezeiten

Wahrscheinlich habt ihr schon gemerkt, dass es die letzten Tage mit den Ladezeiten etwas kritisch war - Timeouts und ewiges Warten waren an der Tagesordnung. Das lag aber nur zum Teil an dem Besucheransturm unter Pokemon GO! (der Server hat mehr als genug Wumms, um das mit leichtigkeit zu stemmen) - sondern vor allem daran, dass Apache 2.4 scheinbar einen Bug hat, in dem die einzelnen Workerprozesse beim Neustart hängen. Im Apache-Status sind die dann als "Graceful Restart" markiert - das ist auch gut so, alle Nase lang sollen die auch neu starten.

Problematisch wird es nur, wenn dieser Neustart dann Minuten braucht. In dieser Zeit können keine Anfragen bearbeitet werden. Und genau das ist passiert. Meine Versuche, da konfigurationsmäßig was zu drehen waren irgendwie auch zum Scheitern verurteilt - daher hab ich dann mit der Holzhammermethode angefangen:


sudo apt install nginx

So. Jetzt gibt's aber eine kleine Sache. Apache hat sogenannte .htaccess-Dateien, mit denen man durch Dateien im Webroot die Konfiguration verändern kann, z.B. für Rewrites. Das will man eigentlich eh nicht haben, weil's knackelangsam ist; aber wir haben davon regen gebrauch gemacht:


delirium@rs213865 /var/www/vhosts % find . -name '.htaccess' | wc -l  
177

177 .htaccess-Dateien. oO

Das ist ein bisschen was. Daher hat's auch zwei Tage gedauert, bis wir das übersiedelt hatten: ich musste jede einzelnde Datei öffnen und die Konfiguration verstehen und neu schreiben. Und deshalb gibt's auch aktuell an manchen Enden noch bröseln - das sind dann subtile Unterschiede darin, wie die Webserver arbeiten - und worauf man beim Konfigurieren aufpassen muss. Sollte sich aber hoffentlich im Laufe des Tages erledigen :)

Cheers,<br /> Delirium

Donnerstag, 9. Juni 2016

How to hack Pokefans!

Packt sqlmap wieder ein - ihr habt doch nicht wirklich gedacht, dass ich euch hier beibringe, wie man Pokefans zerlegt. Nein, wir befassen uns heute mit der guten Definiton von "hacken" - programmieren. Aber wenn ich eine Chance auf Clickbait sehe, dann muss ich sie schon nutzen, irgendwie... :D

Pokefans ist ja ein recht umfangreiches Projekt, und man braucht ein bisschen Startwissen, um hier mitmachen zu können. Bei knapp 1000 Dateien im Repository verliert man vielleicht auch ein bisschen den Überblick. Dafür ist dieser Blogpost da.

Die Struktur des Repositories habe ich ja schon vor einem Jahr (runterscrollen!) beschrieben. Daran hat sich nichts geändert. Vielleicht solltest du das jetzt nochmal lesen. Seither hat sich ein bisschen was getan, die Struktur stimmt aber noch. Um das hier jetzt etwas einfacher zu halten, führe ich den Begriff der Magie ein. Immer, wenn das Verständnis der Mechanik nicht wirklich notwendig ist, um zu verstehen, dass die so ist, wie sie ist, passieren Dinge automagisch oder sind einfach hochgradig uninteressant. Diese Worte werde ich hier noch ein paar mal Verwenden :) Selbstverständlich hat alles einen guten Grund, warum es so funktioniert, wie es funktioniert - vermutlich verwirrt dich das am Anfang aber nur, weil es schwer ist, sich das vorzustellen, ohne das gesamte Framework sehr tief zu kennen. Begnügen wir uns also mal ab und an damit, dass Dinge mit Magie passieren - schließlich ist ja jede hinreichend fortgeschrittene Technik von Magie nicht zu unterscheiden :)

Was setze ich hier voraus? Du solltest grundlegendes Verständnis von folgenden Begriffen haben. Wenn nicht, geh, und schlag' sie nach.

  • Klasse
  • Konstruktor
  • Methode
  • while
  • for
  • foreach
  • if
  • else
  • try
  • catch

Nachdem wir diese grundsätzlichen Sachen jetzt geklärt hätten, können wir ja anfangen!

Schauen wir uns mal einen typischen Konstruktor an. Den hier zum Beispiel.


public HomeController(IBreadcrumbs crumbs, Entities entities, Cache c)
        {
            this.breadcrumbs = crumbs;
            _entities = entities;
            cache = c;
        }

Im Konstruktor bekommt dein Controller ein paar interessante Sachen. Hier bekommst du eine Klasse, die Breadcrumbs verwaltet, den Datenbanklayer (Entities) und den Cache. Das ganze passiert automagisch, wir speichern uns das weg, damit wir das nachher in den Klassenmethoden verwenden können. Falls du dir das nicht merken kannst: Einfach Kopierpaste verwenden. Damit machst du sicher nichts falsch.

Das Wichtigste aus dem Konstruktor sind dabei die Entities. Die sind deine Schnittstelle zur Datenbank. Jede Klasse von hier, die nicht im Migrations-Ordner ist und nicht Entities heißt entspricht einer Tabelle in der Datenbank. Dass sich das zusammenfindet stellt hier Magie sicher. Wir können uns damit begnügen, dass es in der Regel so ist (da gibt's zwar ein, zwei Ausnahmen, aber die sind hochgradig uninteressant).

Wie benutze ich das jetzt? Nun, die Entities haben für jede Datenbanktabelle eine Eigenschaft. Da sind die Daten drin. Und die können wir uns jetzt rausholen. Wenn ich z.B. alle Benutzer haben möchte, deren Name ein 'x' beinhaltet, dann sieht das so aus:


    List<User> users = _entities.Users.Where(x => x.Username.Contains('x')).ToList();

oder kürzer:


    var users = _entities.Users.Where(x => x.Username.Contains('x'));

Was da genau der Unterschied ist, ist für uns erstmal hochgradig uninteressant. Man sollte jedoch beachten, dass die letztere Variante nicht direkt an eine View übergeben werden kann, da müssen wir dann beim Übergeben noch ToList() aufrufen.

Jetzt kann ich auch sagen, ich will nicht eine Liste von Benutzern, sondern ich will einen bestimmten Benutzer, z.B. anhand seiner UserID haben. Das geht so:


    User user = _entities.Users.FirstOrDefault(x => x.UserId == 42);

Fertig.

Innerhalb der Klammer steht ein sogenannter Lambda-Ausdruck. Das ist nichts anderes als die kompakte Schreibweise einer Funktion, die auf jeden Eintrag der Datenbank angewandt wird. Kann man sich so vorstellen - aus dem Lambda aus dem vorherigen Beispiel könne man z.B. das hier machen:


    bool lambda(User x) {
        return x.UserId == 42;
    }

Wenn der Vergleich true wird, dann landet das Element in der Liste, wenn nicht, dann halt nicht. Eigentlich recht einfach, oder?

Wenden wir uns weiterer Magie zu. Jede URL mappt auf eine bestimmte Methode. Wie das passiert, besprechen wir nachher, im Moment begnügen wir uns damit, dass wir gerade eine URL aufgerufen haben, die hier rauskommt. Auch das passiert für uns automagisch und die Details sind hochgradig uninteressant - vorerst.

Wichtig sind hier erstmal zwei Dinge:

  1. Der Rückgabewert der Methode ist vom Typ ActionResult. Das ist eigentlich immer so. Der Name ist irrelevant, sollte aber halbwegs beschreibend sein.
  2. In Zeile 15 haben wir ein return mit einem ewig langen Pfad drin. Dieser Pfad gibt an, wo die View (also die Anzeigevorschrift) für diese Methode ist. Dort ist dann das HTML-Template, das die Daten aus dem Controller in HTML verwandelt.

Wenn der Computer also das abarbeitet und beim return ankommt, landen wir dann hier. Das ist die dazugehörige View. Findest du den Pfad wieder? Ja? Gut. Der Inhalt hier ist auch nicht sonderlich spannend, zeigt aber, wie die Abfolge grunsätzlich läuft: Url -> Controller-Methode -> View -> fertig.

Jetzt ist so eine statische Seite relativ langweilig. Ich will Daten übergeben! Dazu schauen wir uns ein etwas komplizierteres Beispiel an - etwa das hier. Das hier ist ein Auszug aus der neuen Fanart-Verwaltung. Halten wir mal folgendes fest:

  1. Zeile 47 holt sich Daten aus der Datenbank. Wie genau - hochgradig uninteressant für den Moment. Wir holen uns alle Fanarts mit zugehörigen Tags, die vom gerade angemeldeten Benutzer hochgeladen wurden, sortiert nachdem Zeitpunkt des Hochladens, neuere zuerst.
  2. Zeile 48 holt die Namen der Fanart-Kategorien aus dem Cache. Das geht schneller als der Datenbankzugriff. Details dazu sind wieder hochgradig uninteressant.
  3. Zeile 50 übergibt die Daten an die View. Preisfrage: Worin besteht denn der unterschied zur vorherigen Zeile?

Da kommen wir dann hier raus. Gerendert sieht das dann z.B. so aus. Quellcode.

Dabei geben wir in der View in Zeile 2 den Typ der Variable an, die wir vorhin übergeben haben - hier eine Liste. Diese Variable heißt innerhalb der View Model. Der geneigte Leser möge jetzt über folgende Fragestellungen nachdenken:

  • Wo fängt die View im Quellcode an, wo hört sie auf?
  • Wo sind die Schleifen hinverschwunden? Was haben sie generiert?
  • Was macht Url.Action(...)?
  • Was könnte @variable machen?

Hier die Auflösung:

  • Die View geht von Zeile 70 bis Zeile 104.
  • Die äußere Schleife hat den Code von Zeile 82 bis 102 generiert. Wäre mehr als eine Fanart hochgeladen, würde sich dieser Block entsprechend wiederholen.
  • Die innere Schleifen haben Z85 - 89 respektive Z93-98 generiert. Die zweite Schleife macht dabei nichts anderes, als die Fließkommazahl der Bewertung als Sterne darzustellen.
  • Url.Action(...) generiert einen Link. Wir geben dabei Klasse und Methode des Ziels an, die URL wird anhand der Mappingtabelle gefunden
  • @variable gibt Variablen aus - und zwar XSS-Sicher, alles HTML wird escaped, damit es keinen Schaden anrichten kann.

Wie mappe ich jetzt eine URL zu einer Methode in einer Klasse? In unserem Beispiel sind wir im ManageController und unsere Methode heißt Index. Das zugehörgige Mapping gibts hier:


            context.Routes.Add("fanartManage", new DomainRoute(
                            "fanart." + ConfigurationManager.AppSettings["Domain"],
                            "verwaltung",
                            new { action = "Index", controller = "Manage" },
                            dataTokens
                ));

Auch hier passiert wieder viel automagisch, was uns nicht weiter kümmern braucht. Wichtig ist nur folgendes:

  1. Der Name - hier fanartManage - muss eindeutig sein. Keine Sorge, es fliegt dir um die Ohren, wenn es nicht eindeutig ist.
  2. Die korrekte Subdomain muss angegeben werden.
  3. Die Url nach dem / muss angegeben werden. Da wir hier bei http://fanart.pokefans.rocks/verwaltung/ rauskommen wollen, ist das nur "verwaltung" (beachte, dass ich keinen abschließenden / gesetzt habe!).
  4. Action ist der Name der Methode, die aufgerufen werden soll, wenn jemand diese URL ansurft. Controller ist die Klasse, in der sich die Methode befindet. Beachte, dass die Klasse in Wahrheit ManageController heißt und sich im Unterordner "Controllers" der entsprechenden Area befindet. Das muss so sein, da fährt die Eisenbahn drüber.
  5. die DataTokens musst du einfach reinkopieren. Was die machen, ist hochgradig uninteressant.

Tja, dann bleibt mir auch nicht mehr viel zu sagen, außer: happy hacking. Und falls es noch fragen gibt... einfach melden. Es gibt keine dummen Fragen.

Sonntag, 7. Februar 2016

Warum man nicht um 23:00 nach einer langen Nachtschicht an kritischen Systemen basteln sollte

Einige von euch haben es vielleicht gemerkt - für ein paar Minuten leiteten alle Artikel auf eine Seite zu den Jubiläumsaktionen weiter. Was war passiert?

Nun, prinzipiell kann unser Artikelsystem mehrere Urls auf einen Artikel mappen. Das heißt der Inhalt "Test" könnte z.b. unter pokefans.net/eine/kategorie/test und unter pokefans.net/andere/kategorie/mehrtest erreichbar sein. Dazu sind beide URLs in der Datenbank gespeichert. Für SEO ist es aber wichtig, dass eine URL auf eine Resource zeigt, d.h. jeder Artikel hat eine Primär-URL, und alle anderen leiten auf diese weiter. Damit ändert sich auch die URL im Browser.

Wenn ein neuer Artikel angelegt wird, werden dafür auch alle URLs gespeichert. Ebenfalls aus SEO-Gründen dürfen einmal angelegte URLs so einfach nicht mehr geändert werden. Daher muss ich das in der Datenbank direkt machen.

Das sieht dann z.B. so aus:


UPDATE inhalte SET url = 'neue/url/zum/artikel' WHERE id = 12345;

(Das ist selbstverständlich nur ein Beispiel)

So. Was war passiert? Ich update vor mich hin und habe die WHERE-Klausel vergessen. Das heißt, mein Befehl trifft nicht eine Zeile, sondern alle. Ups.

Ich sollte wirklich nicht mehr um 23:00 an der Datenbank herumfuhrwerken. Gottseidank konnte ich das recht einfach wiederherstellen...

~Delirium

Donnerstag, 20. August 2015

Entwicklung mit monodevelop auf Arch GNU/Linux

Nachdem ich jetzt einige Zeit mit programmieren unter Arch Linux verbracht habe - wie einige aufmerksame Leser vielleicht durch unseren IRC-Channel mitbekommen haben - will ich hier kurz meine Erfahrungen damit niederlegen. Vielleicht kann ja der ein oder andere damit was anfangen - und am besten Nachmachen.

Vorbereitungen

Damit unser Zeug überhaupt unter Linux läuft, braucht man ein aktuelles Mono. Aktuell heißt in dem Fall: mindestens Version 4.3. In den Repositories ist derzeit 4.0.2 - und das ist aus mehreren Gründen zu alt:

  1. Ihm fehlt mein Patch für System.Web
  2. es hat noch nicht das Reference-Source System.Data (und ist damit inkompatibel zu Glimpse)

Was also tun? Die Antwort hierauf ist einfach: ein parallel Mono environment aufsetzen! Leider ist die dazugehörige Anleitung nicht mehr ganz aktuell, daher mag ich das ganz kurz zusammenfassen. Vorausgesetzt wird ein bereits installiertes mono-complete aus den Repositories deiner Linux-Distribution.

mono kompilieren


git clone https://github.com/mono/mono
cd mono
./configure --prefix=/opt/mono
make
sudo make install

Wir holen uns Mono von github, konfigurieren es den Präfix /opt/mono zu verwenden, bauen es und installieren es nach /opt/mono.

Das dauert jetzt seine zeit. Hol dir inzwischen einen Kakao und ein paar Kekse. Alles fertig? Gut. Dann können wir ja weitermachen. Jetzt kompilieren wir xsp, gnome-sharp und gtk-sharp. Dazu wechseln wir zu allererst ins Parallel Mono Environment:


#!/usr/bin/env bash

MONO_PREFIX=/opt/mono
GNOME_PREFIX=/opt/gnome
export DYLD_FALLBACK_LIBRARY_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_FALLBACK_PATH
export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=$MONO_PREFIX/include:$GNOME_PREFIX/include
export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal
export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig:$GNOME_PREFIX/lib/pkgconfig
export PATH=$MONO_PREFIX/bin:$PATH

Speichere dieses Skript als mono-parallel.sh ab und source es. Und zwar so (direktes Aufrufen bringt nichts):


source mono-parallel.sh

Mono müsste sich jetzt als Version 4.3 (oder neuer) melden, wenn nicht, hast du etwas falsch gemacht:


mono --version

Passt alles, können wir fortfahren:


git clone https://github.com/mono/gnome-sharp
cd gnome-sharp
git checkout 2.24.1
./bootstrap-2.24 --prefix=/opt/mono
make
sudo make install

Genauso verfahren wir mit gtk-sharp:


git clone https://github.com/mono/gtk-sharp
cd gtk-sharp
git checkout 2.24.1
./bootstrap-2.24.1 --prefix=/opt/mono
make
sudo make install

Zu guter Letzt kommt jetzt noch xsp:


git clone https://github.com/mono/xsp
cd xsp
./configure --prefix=/opt/mono
make
sudo make install

Jetzt sind wir Startklar. Überprüfe, ob monodevelop startet, indem du es direkt in der Kommandozeile aufrufst. Passt alles, kannst du es wieder schließen und dieses Skript nach /usr/bin/monodevelop-gitmono speichern:


#!/usr/bin/env bash

MONO_PREFIX=/opt/mono
GNOME_PREFIX=/opt/gnome
export DYLD_FALLBACK_LIBRARY_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_FALLBACK_PATH
export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH
export C_INCLUDE_PATH=$MONO_PREFIX/include:$GNOME_PREFIX/include
export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal
export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig:$GNOME_PREFIX/lib/pkgconfig
export PATH=$MONO_PREFIX/bin:$PATH

monodevelop

Damit startest du monodevelop mit dem guten Mono einfach per Doppelklick auf das Skript oder Aufruf von


monodevelop-gitmono &

aus der Kommandozeile.

Wie arbeitet es sich jetzt mit monodevelop?

Nun, ich bin Visual Studio gewöhnt. Man merkt, dass VS das wesentlich ausgereiftere Produkt ist, aber das ist auch nicht weiter verwunderlich. Monodevelop steht mir aber nicht im Weg herum, nachdem ich alle Keybindings auf meine gewohnten angepasst habe, den Whitespace und Codestyle richtig gestellt hatte, lief alles wie am Schnürchen. Monodevelop kommt mir - bis auf die Tatsache, dass es Swapping noch weniger verträgt als Visual Studio - sogar einen tacken schneller vor. Aber da kann ich mich auch täuschen.

Sehr angenehm ist auch, dass xsp4 sich nicht um irgendwelche Hostnamen schert, wie der IISExpress oder der große IIS. Eine Fehlerquelle weniger.

Etwas nervig ist nur, dass z.B. für die EntityFramework-Migrationen keine direkte Integration gegeben ist. Unser dbtool ist funktional noch nicht gänzlich äquivalent. Migrationen müssen auf eine spezielle Weise im Projekt eingetragen sein, damit alles funktioniert. Ich weiß nicht, wie man das in der GUI macht, daher muss ich bis jetzt immer nach dem Hinzufügen einer Migration den Foo selbst in der Projektdatei ausbessern. Das kann man aber fixen; so kompliziert ist das nicht (das sind alles XML-Dateien). Man müsste es nur implementieren. Falls du helfen willst, melde dich einfach bei mir.

So long, Delirium

Sonntag, 9. August 2015

Pokefans auf GNU/Linux

Kurzes Statusupdate:

gerade eben habe ich ein kleines dbtool auf GitHub gestellt. Mit diesem kann man die Datenbankmigrationen auch unter GNU/Linux anwenden sowie neue Migrationen erstellen.

Cheers,
Delirium

Mittwoch, 29. Juli 2015

Watch me Code auf dem Pokefans'schen Twitch-Kanal!

Hallo zusammen,

ab und an werde ich jetzt Watch Me Code-Sessions auf dem Pokefans-Twitch-Kanal abhalten. Da könnt ihr mir beim programmieren zusehen (live, in Farbe und mit Stereo!) sowie Fragen stellen, die ich dann hoffentlich beantworten kann. Wer also immer schonmal wissen wollte, wie so Projekte eigentlich entstehen, einfach einschalten:

http://www.twitch.tv/pokefansnet

Cheers,

Delirium

Update:

Die Codefiles der heutigen Session sind jetzt auf Github. Danke fürs Zusehen!

Dienstag, 9. Juni 2015

Und los geht's!

Vor knapp einer Woche haben wir euch die Anfänge vom neuen Pokefans vorgestellt. Einige haben sich das auch schon angesehen und haben nachgefragt. Daher möchte ich diesen Blogpost mal nutzen, um auf ein paar Fragen einzugehen.

Q: Warum C? Ich habe gehört, dass das viel schwieriger sein soll als Java/PHP/<insert sprache deiner Wahl hier>

A: Nun, C ist in der Tat schwerer. Aber wir verwenden ja auch C#, nicht C. C# ähnelt mehr Java als C, mit dem Unterschied, dass die Standardbibliothek wesentlich besser ist als bei Java. Das ist sozusagen die Gunst der späten Stunde; Java gibt es auch schon um einiges länger als C#.

Q: Visual Studio 2013 läuft bei mir nicht!!!11

A: Du benötigst mindestens Windows 7 SP1 oder neuer, um mit Visual Studio zu arbeiten. Keine Sorge: um bei uns mitzuentwickeln muss niemand Geld ausgeben. Du kannst einfach eine der vielen Linux-Distributionen nehmen und mit MonoDevelop arbeiten. Das geht genauso! Und Linux ist sowieso das bessere Betriebssystem ;-)

Q: Mono gibt mir eine MissingMethodException - was ist das?

A: Dein Mono ist wahrscheinlich einfach zu alt. Damit alles läuft, brauchst du ein Bleeding-Edge-Mono (in Wahrheit wurde nämlich mein Pull-Request, der Mono patcht, sodass alles läuft erst heute, 9.6.2015 gemerged, kann also sein, dass es noch etwas dauert, bis du das in deinem Repository hast).

Q: Gott sind das viele Dateien. Wo fang' ich denn da an?

A: Weiterlesen!

Einen Einstieg ins Repository finden

Wahrscheinlich haben viele von euch noch nie ein Projekt dieser Größe gesehen. Daher will ich mal zusammenfassen was wo ist. Das ist wahrscheinlich nicht immer ganz korrekt, gibt aber einen Überblick darüber, wie wir uns das vorstellen.

Generell gibt es erstmal fünf Projekte. Cache, Security, Data, Util und Pokefans. Doch was tun die genau?

  • Data ist unser Datenabstraktionslayer. Hier befinden sich die CodeFirst-Klassen von Entity Framework. Mehr Infos dazu gibt's z.B. hier.
  • Cache ist eine Sammlung von verschiedenen Caching-Backends. Das ist sogar in einem recht fertigen Zustand - danke an dieser Stelle an Birne94, von dem die Implementation stammt.
  • Security beherbergt alles, was mit Benutzerauthentifizierung und Berechtigungsprüfung zu tun hat. Insbesondere die OWIN-Provider. Kann man auch soweit fertig nennen.
  • Util ist eine Sammlung von verschiedenen Klassen, die als "Helfer" für das Pokefans-Projekt fungieren. Wer hier was rein tut, sollte sich i.d.R. sicher sein, dass da auch rein gehört - aka: wir werden genau kontrollieren, was da drin landet. Bis jetzt ist das u.A. ein Breadcrumb-Helper sowie eine angepasste Seiten-Basisklasse, die mehr Eigenschaften hat als die Standardklasse. Unter anderem stellen wir damit den derzeit aktiven Benutzer zur Verfügung.
  • Pokefans ist das eigentliche MVC5-Projekt. Darin ist dann die gesamte Webseite gebaut.

MVC steht für Model-View-Controller, ein Designpattern, das bei Webanwendungen sehr beliebt ist. Grundsätzlich trennt das damit in Datenmodelle (Data-Projekt), Controller (sozusagen eine Arbeitsschicht: hier wird festgelegt, was die Seite tut) und Ansichten (die Präsentationsschicht: Wie sieht das Ergebnis des Controllers aus?). Zusätzlich teilen wir das Ganze noch in verschiedene Bereiche auf. Diese findest du im Unterordner "Areas" des Pokefans-Projekts. Generell kannst du dir eine Area als eine Subdomain vorstellen - ein zusammenhängendes Stück, dass man auch isoliert betreiben könnte, wie den Benutzerbereich oder die Fanartgalerie.

Startbereit!

Jetzt, wo wir wissen, wie das Repository aufgebaut ist, können wir doch einfach mal eine Seite hinzufügen. Weil ich von Praxisfernen Beispielen nichts halte und meine Arbeitszeit wertvoll ist, habe ich gleich etwas gemacht, was ich sowieso grade brauche: Eine Startseite für den Mitarbeitsbereich.

Wo arbeiten wir also? Der Mitarbeitsbereich hat eine eigene Subdomain: mitarbeit.pokefans.net. Wir haben also eine eigene Area mitarbeit. Dort gibt's mal grundsätzlich drei Ordner:

  • Controllers
  • Models
  • Views

In den meisten Fällen wirst du den Models-Ordner nicht brauchen. Falls doch, bedenke, dass die Klassen die sich da drin befinden nicht direkt in der Datenbank gespeichert werden können. In diesem Beispiel können wir ihn aber ignorieren, da wir erstmal nicht mit Daten hantieren. Aus der vorherigen Erklärung - und hoffentlich Googlei von deiner Seite aus ;-) - von MVC wissen wir, dass eine Seite mindestens zwei Dateien braucht: einen Controller und eine View. Das ist zwar nicht ganz richtig, da eine Controlleraction auch Weiterleitugnen machen kann (dann brauche ich keine View), aber für uns reicht erstmal zu wissen, dass es zu jeder Seite einen Controller und eine View gibt.

Lass uns das noch etwas präzisieren: Jede Url in MVC wird zu einer Methode aufgelöst, die dann aufgerufen wird. Genau genommen brauchen wir also nicht einen Controller, sondern eine Controllermethode. Zusammengehörende Controllermethoden fasst man in einem Controller zusammen. Und nachdem ich dich jetzt genug mit Theorie gelangweilt habe, gibt's jetzt erstmal etwas Code. Als erstes legen wir mal unseren Controller an. Weil Programmierer faule Menschen sind, hat Visual Studio dafür schon eine schöne Klickibunti-Gui. Wer mit Monodevelop arbeitet, muss selbst Hand anlegen - ist aber nicht weiter schwer. Ein Controller ist nichts anderes als eine Klasse, die im Controllers-Ordner sitzt, und einen Namen wie DeinNameHierController.cs hat. Wichtig ist, dass das mit Controller.cs aufhört, sonst geht gar nichts. Wir legen also einen leeren Controller an und nennen ihn DashboardController.cs. Das schaut dann so aus:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Pokefans.Areas.mitarbeit.Controllers
{
    public class DashboardController : Controller
    {
        // GET: mitarbeit/Dashboard
        public ActionResult Index()
        {
            return View();
        }
    }
} 

Das ist ja schon mal super. Zwei kleine Änderungswünsche habe ich aber noch:

  1. Den Mitarbeiterbereich sollen natürlich nur Mitarbeiter betreten dürfen. Daher müssen wir noch entsprechend die Berechtigung überprüfen.
  2. Die automatische View-Auflösung funktioniert leider nicht wie gewünscht. Daher müssen wir den Pfad zur View manuell angeben.

Zu Berechtigungsprüfung können wir einfach ein Attribut für die Klasse verwenden. Über die Klasse schreiben wir also folgendes:

[Authorize(Roles="mitarbeiter")]

Und schwupps - jetzt können alle Actions (also alle Methoden in dem Controller) nur noch aufgerufen werden, wenn der aufrufende Benutzer die Rolle (oder auch: Berechtigung) "mitarbeiter" hat. Um die View explizit anzugeben, muss dann noch die Zeile 15 wie folgt angepasst werden:

           return View("~/Areas/mitarbeit/Views/Dashboard/Index.cshtml");

Da steht dann auch schon drin, wo wir unsere View anlegen müssen. Zunächst einmal legen wir für den Controller ein neues Unterverzeichnis in unserem View-Ordner an. Darin platzieren wir jetzt eine neue View-Datei (.cshtml) und nennen sie wie die dazugehörige Methode.

Ich will jetzt nicht zu sehr auf die Syntax der Views eingehen - das kann man im Web besser nachlesen. So könnte z.B. unsere View aussehen:

@{
    SiteTitle = "Mitarbeitsbereich - Dashboard";
    SiteHeader = "Dashboard";
    SiteDescription = "Deine Übersicht";
    Layout = "~/Areas/mitarbeit/Views/_Layout.cshtml";
}

<div class="panel panel-default">
    <div class="panel-heading"><h3 class="panel-title">Hallo!</h3></div>
    <div class="panel-body">
        Ich befinde mich derzeit noch im Aufbau. Schau bald wieder vorbei!
    </div>
</div>

Ein paar Sachen muss ich dazu noch festhalten:

  • Mit SiteTitle lege ich den Titel in der Seite (<title>-Tag im Head) fest.
  • SiteHeader bestimmt die erste Überschrift
  • SiteDescription einen zusätzlichen Text dazu. Das wird aber nicht überall benutzt.
  • Layout gibt eine Layout-Masterseite an. Das muss nur explizit gesetzt werden, wenn (wie im Mitarbeitsbereich) nicht das Standardlayout verwendet wird.

Ich hoffe, das hat jetzt einigen den Einstieg in das Projekt erleichtert. Demnächst steht der Code hier auch auf GitHub, wenn's soweit ist, gibt's hier ein Update.

Rock on, ~Delirium

Update: Der Code ist jetzt auf GitHub gelandet!

Mittwoch, 3. Juni 2015

Pokefans goes Github!

Vor zwei Wochen stellten wir euch das neue Entwicklungskonzept von Pokefans.net vor. Nun ist es endlich soweit! Ab sofort wird der Entwicklungsprozess öffentlich auf der open-source-Plattform GitHub durchgeführt. Dies bedeutet nicht nur, dass ihr nun den aktuellen Stand stets verfolgen könnt, sondern ihr könnt Pokefans nun auch auf technischer Seite aktiv vorantreiben!

Das Repository des neuen Kerns "Mighty Milotic" findet ihr hier auf GitHub.

Wer gleich loslegen will und sich den bestehenden Code zu Gemüte führen möchte, der braucht zunächst ein paar Tools. Eine Anleitung für die verschiedenen Systeme haben wir auf dieser Seite für euch zusammengestellt. Wie an allen anderen Stellen steht es euch hier natürlich frei, die Liste zu erweitern oder Stellen zu dokumentieren, an denen es Probleme gab. Auf diese Weise könnt ihr zukünftigen Mitarbeitern helfen, die an der gleichen Stelle Probleme hatten.

Damit die Entwicklung reibungslos vorangehen kann gibt es natürlich ein paar Spielregeln, an die sich jeder Entwickler halten muss. Diese wollen wir im Folgenden kurz anreißen. Als allgemeine Orientierung kann euch der bestehende Code dienen.

  • Schreibt guten und dokumentierten Code! Bei Pokefans handelt es sich um ein großes Projekt, dessen Code über einen langen Zeitraum eingesetzt und gewartet werden soll. C# bietet die Möglichkeit, Klassen und Funktionen durch XML-Kommentare zu dokumentieren. Hier wäre es gut, wenn diese zu jeder Funktion existieren. Zu detaillierten Style-Kriterien des Codes wird in den nächsten Tagen noch ein Dokument in das Repository geladen, indem ihr genauere Informationen findet.
  • Jeder von euch kann neue Features hinzufügen oder verbessern, allerdings müssen diese zunächst durch ein Mitglied des Entwicklerteams überprüft werden. Dies geschieht, indem ihr euch zunächst das Projekt auf GitHub klont. Nachdem ihr dann die Änderungen vorgenommen habt, könnt ihr einen Pull-Request einreichen. Dieser kann dann in das bestehende System übernommen werden.
  • Alle Programmteile müssen sorgfältig getestet werden. Wir setzen bei der Entwicklung auf Unit-Tests und im Idealfall sollte jede Funktion durch einen Test abgesichert werden. Dies verhindert, dass die Arbeit an einer Stelle unabsichtlich etwas anderes zerstört.
  • Wenn ihr einen Fehler findet, so könnt ihr in ihm Issue-Tracker von GitHub eintragen. Es steht euch natürlich frei, diesen auch selbst zu beheben. In diesem Fall wäre es toll, wenn dazu gleich ein Test geschrieben wird, sodass dieser Fehler beim nächsten Mal abgesichert ist!

Selbst wenn ihr noch dabei seid, das Programmieren zu lernen, ist dies kein Problem! Wir stehen euch im Forum oder im IRC-Chat bei Fragen zur Verfügung.

In den folgenden Wochen werden wir den bestehenden Kern erweitern und auch weitere Dokumentationen veröffentlichen. Diese findet ihr dann zum einen hier im Blog, aber auch auf GitHub im Repository.

Wie immer könnt ihr euch bei Fragen gerne an uns wenden, entweder direkt hier per Kommentar oder per PN im Forum.

~Birne

Mittwoch, 20. Mai 2015

Pokefans wird neu - und offen!

Pokefans existiert nun seit über 7 Jahren. In dieser Zeit hat sich einiges getan. War am Anfang der Funktionsumfang noch relativ überschaubar - als einzige große Systeme existierten damals Pokedex und Artikelsystem - kamen recht bald weitere größere Systeme dazu, z.B. die Tauschbörse oder das Fanart-System.

Mit dem ersten Serverumzug 2008 und der Umbenennung von Pokemon-Inside zu Pokefans wurde auch ein programmiertechnischer Neustart vorgenommen; mit dem Ziel alles ins neue System zu übernehmen. Trotzdem läuft dieses System - wenn auch grundlegend entschlackt - bis heute weiter. Diese Codebasis datiert zurück bis 2006, ist also unter heutigen Gesichtspunkten ziemlich veraltet und läuft auch nur noch mit einiger Magie auf unserem Server. Auch um das "neue" System ist es nicht wesentlich besser bestellt. Im laufe der Zeit wurde die API mehrmals umgeschmissen, sodass unterschiedliche Teile der Seite komplett unterschiedlich funktionieren - mangels Testcases und Manpower wurde die alte API aber nie wirklich abgeschafft, und bevor alles portiert war wurde die API wieder entsorgt, sodass die unterschiedlichen APIs schlussendlich explodierten.

Vielleicht wird der ein- oder andere jetzt anmerken, dass man von all dem nichts gemerkt hat. Dann freut mich das natürlich. Aber sichtbar war es durchaus - die verschiedenen Pokedex-Layouts und Informationsstände vor der Pokedex-Vereinheitlichung waren z.B. eine direkte Konsequenz daraus. Auch für ein Web, in dem immer mehr Clientseitig passiert, war das System nicht ausgelegt.

Was also tun? Die Lösung ist eigentlich sehr naheliegend: neu schreiben.

Damit wir uns die Arbeit erleichtern und uns auf das konzentrieren, was eigentlich wichtig ist - nämlich unsere Anwendung - war relativ schnell klar, dass wir auf Standardtechnologien setzen wollen. Nun hat die Wahl eines Frameworks aber zwangsläufig zur Konsequenz, dass man sich vorher über die zu verwendende Programmiersprache einig wird. Relativ schnell war klar, dass eine komplett neue Lösung nicht mehr auf php aufbaut - der Hauptgrund war hier übrigens, dass php immer noch kein vernünftiges Unicode-Handling hat, und sich auch nicht abzeichnet, dass da in naher Zukunft was passiert. Oh, und das oder das natürlich.

Was bleibt uns also übrig - von den populäreren Sprachen?

  • NodeJS: Damit kennt sich keiner von uns so wirklich aus. Und javascript ist nur marginal besser als php. Damit das wirklich einsetzbar wird, landet man dann aber erst recht wieder bei irgendeiner Sprache, die zu JS kompiliert (z.B. Dart) und das ist IMO so grausam wie es sich anhört.
  • Python: Es gibt zwar Django und Flask, aber wirklich gute Möglichkeiten, da Refactoring zu betreiben, gibt's nicht.
  • ASP.NET: Läuft unter Mono recht gut. Und mit der neuen Version wird's dann auch direkt unter Linux unterstützt.
  • Java/JSP: Nimmt sich nicht viel zu ASP.NET, außer dass Java eine kaputte Standardlibrary hat, und von den Sprachfeatures C# weit hinterher ist.

Insofern: Tschüss PHP, hallo ASP.NET! Damit war dann auch die Frameworkfrage geklärt - ASP.NET MVC regelt. Genauer gesagt bauen wir jetzt auf diesen Technologien auf:

  • ASP.NET Identity
  • ASP.NET MVC5
  • OWIN
  • Unity Dependency Injection
  • EntityFramework 6 Code First
  • MariaDB (MySQL-Fork) als Datenbank-Backend
  • NUnit als Unit-Testing-Framework

Vielleicht fragt sich jetzt der eine oder andere "Das ist ja alles schön und gut, aber warum erzählst du uns das so genau?" Nun: jeder soll mitmachen können. Wir haben uns entschlossen, den Code unter AGPL zu veröffentlichen - das heißt: jeder darf ihn runterladen, laufen lassen, verändern, muss aber selbst immer den Quellcode bereitstellen, selbst wenn es nur als Dienst irgendwo gehostet wird. Demnächst stellen wir den Code auf Github - wenn das soweit ist, werden wir es natürlich hier ankündigen. Da gibt's dann auch die Informationen zum Workflow und wie man richtig beiträgt.

Cheers, Delirium

PS: Und wer jetzt irgendwas nicht verstanden hat und genauer erklärt haben möchte... schreibt's in die Kommentare! *nachuntenzeig*

Mittwoch, 22. April 2015

inside.pokefans.net 2.0

Nun, hier sind wir wieder. Nachdem bei der großen Umfrage sich doch einige einen development-Blog gewünscht haben, werden wir hier also etwas aus dem Nähkästchen plaudern.

Was erwartet euch also hier? Nun, das wissen wir selbst noch nicht so genau. Wir wollen euch jedenfalls Einblicke in die aktuelle Entwicklung an Pokefans geben. Etwas über dieses und jenes schreiben, die Architektur etwas erklären. Wahrscheinlich wird es oft sehr technisch. Aber technisch ist gut.

Damit das Ganze funktioniert, müsst ihr natürlich mitmachen. Wer einen Themenvorschlag hat, immer her damit. Das hier ist ein Service für eure Informationslust. Je genauer ihr uns sagt, was euch interessiert, desto besser werden wir unsere Blogeinträge darauf abstimmen können ;-)

~ Delirium