diff --git a/.gitignore b/.gitignore
index 31a28420bcf271c00965ef478bf3567d705ac5a7..451f1e67f8cc4d245fac3ea107c16cbc0d51a1d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,8 @@ pv/site_media/buttons/b-*.png
 pv/site_media/buttons/s-*.png
 pv/site_media/buttons/r-*.png
 pv/site_media/show_images/*
+pv/site_media/note_images/*
+pv/site_media/user_images/*
+pv/site_media/__sized__/*
 pv/cache/*
 pv/mysql.cnf
\ No newline at end of file
diff --git a/profile/__init__.py b/profile/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/profile/admin.py b/profile/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..281aa6eee7e0e65ce99675fc9d3ef07aeb60f34a
--- /dev/null
+++ b/profile/admin.py
@@ -0,0 +1,22 @@
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.auth.models import User
+
+from .models import Profile
+
+class ProfileInline(admin.StackedInline):
+    model = Profile
+    can_delete = False
+    verbose_name_plural = 'Profile'
+    fk_name = 'user'
+
+class CustomUserAdmin(UserAdmin):
+    inlines = (ProfileInline, )
+
+    def get_inline_instances(self, request, obj=None):
+        if not obj:
+            return list()
+        return super(CustomUserAdmin, self).get_inline_instances(request, obj)
+
+admin.site.unregister(User)
+admin.site.register(User, CustomUserAdmin)
\ No newline at end of file
diff --git a/profile/migrations/0001_initial.py b/profile/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..cca279bd3d4f730051f677893fc262ad5e91ac69
--- /dev/null
+++ b/profile/migrations/0001_initial.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.3 on 2017-11-09 18:42
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import tinymce.models
+import versatileimagefield.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Profile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('biography', tinymce.models.HTMLField(blank=True, null=True, verbose_name='Biography')),
+                ('website', models.URLField(blank=True, verbose_name='Website')),
+                ('googleplus_url', models.URLField(blank=True, verbose_name='Google+ URL')),
+                ('facebook_url', models.URLField(blank=True, verbose_name='Facebook URL')),
+                ('twitter_url', models.URLField(blank=True, verbose_name='Twitter URL')),
+                ('linkedin_url', models.URLField(blank=True, verbose_name='LinkedIn URL')),
+                ('youtube_url', models.URLField(blank=True, verbose_name='Youtube URL')),
+                ('dorftv_url', models.URLField(blank=True, verbose_name='DorfTV URL')),
+                ('cba_url', models.URLField(blank=True, verbose_name='CBA URL')),
+                ('cba_username', models.CharField(blank=True, max_length=60, verbose_name='CBA Username')),
+                ('cba_user_token', models.CharField(blank=True, max_length=255, verbose_name='CBA Token')),
+                ('ppoi', versatileimagefield.fields.PPOIField(default='0.5x0.5', editable=False, max_length=20, verbose_name='Image PPOI')),
+                ('height', models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Height')),
+                ('width', models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Width')),
+                ('image', versatileimagefield.fields.VersatileImageField(blank=True, height_field='height', null=True, upload_to='user_images', verbose_name='Profile picture', width_field='width')),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'db_table': 'profile',
+            },
+        ),
+    ]
diff --git a/profile/migrations/__init__.py b/profile/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/profile/models.py b/profile/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..40a04a8668ddca8cade034ebb1ec5bf8c7e70251
--- /dev/null
+++ b/profile/models.py
@@ -0,0 +1,43 @@
+from django.core.exceptions import ObjectDoesNotExist
+from django.db import models
+from django.contrib.auth.models import User
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.utils.translation import ugettext_lazy as _
+from versatileimagefield.fields import VersatileImageField, PPOIField
+from django.conf import settings
+import os
+
+from tinymce import models as tinymce_models
+
+class Profile(models.Model):
+    user = models.OneToOneField(User, on_delete=models.CASCADE)
+    biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True)
+    website = models.URLField(_("Website"), blank=True)
+    googleplus_url = models.URLField(_("Google+ URL"), blank=True)
+    facebook_url = models.URLField(_("Facebook URL"), blank=True)
+    twitter_url = models.URLField(_("Twitter URL"), blank=True)
+    linkedin_url = models.URLField(_("LinkedIn URL"), blank=True)
+    youtube_url = models.URLField(_("Youtube URL"), blank=True)
+    dorftv_url = models.URLField(_("DorfTV URL"), blank=True)
+    cba_url = models.URLField(_("CBA URL"), blank=True)
+    cba_username = models.CharField(_("CBA Username"), blank=True, max_length=60)
+    cba_user_token = models.CharField(_("CBA Token"), blank=True, max_length=255)
+    ppoi = PPOIField('Image PPOI')
+    height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
+    width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
+    image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='user_images', width_field='width', height_field='height', ppoi_field='ppoi')
+
+    def __str__(self):
+        return self.user.username
+
+    class Meta:
+        db_table = 'profile'
+
+    def save(self, *args, **kwargs):
+        super(Profile, self).save(*args, **kwargs)
+
+        # Generate thumbnails
+        if self.image.name and settings.THUMBNAIL_SIZES:
+            for size in settings.THUMBNAIL_SIZES:
+                thumbnail = self.image.crop[size].name
\ No newline at end of file
diff --git a/profile/urls.py b/profile/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ac3efa351fc14074722a1b54094f59a3b786351
--- /dev/null
+++ b/profile/urls.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+
+admin.autodiscover()
+
+urlpatterns = []
\ No newline at end of file
diff --git a/program/admin.py b/program/admin.py
index 2ea867845224199b9577f76a7fbf98a7a4437497..df279ff9c652fa2c470551d2c04f1d58228c112a 100644
--- a/program/admin.py
+++ b/program/admin.py
@@ -2,6 +2,7 @@ from django.core.exceptions import ObjectDoesNotExist
 from django.contrib import admin
 from django.utils.translation import ugettext_lazy as _
 from django.shortcuts import render
+from django.conf import settings
 
 from .models import Type, MusicFocus, Category, Topic, RTRCategory, Host, Note, RRule, Schedule, Show, TimeSlot
 from .forms import MusicFocusForm, CollisionForm
@@ -94,6 +95,7 @@ class NoteAdmin(admin.ModelAdmin):
     def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
         four_weeks_ago = datetime.now() - timedelta(weeks=4)
         in_twelf_weeks = datetime.now() + timedelta(weeks=12)
+
         if db_field.name == 'timeslot':
             try:
                 timeslot_id = int(request.get_full_path().split('/')[-2])
@@ -107,8 +109,11 @@ class NoteAdmin(admin.ModelAdmin):
         return super(NoteAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
     def save_model(self, request, obj, form, change):
+        if not change:
+            obj.user = request.user
         obj.save()
 
+
 class TimeSlotInline(admin.TabularInline):
     model = TimeSlot
     ordering = ('-end',)
@@ -117,7 +122,7 @@ class TimeSlotInline(admin.TabularInline):
 class ScheduleAdmin(admin.ModelAdmin):
     actions = ('renew',)
     inlines = (TimeSlotInline,)
-    fields = (('rrule', 'byweekday'), ('dstart', 'tstart', 'tend'), 'until', 'is_repetition', 'automation_id')
+    fields = (('rrule', 'byweekday'), ('dstart', 'tstart', 'tend'), 'until', 'is_repetition', 'automation_id', 'fallback_playlist')
     list_display = ('get_show_name', 'byweekday', 'rrule', 'tstart', 'tend', 'until')
     list_filter = (ActiveSchedulesFilter, 'byweekday', 'rrule', 'is_repetition')
     ordering = ('byweekday', 'dstart')
@@ -155,9 +160,9 @@ class ShowAdmin(admin.ModelAdmin):
     prepopulated_fields = {'slug': ('name',)}
     search_fields = ('name', 'short_description', 'description')
     fields = (
-        'predecessor', 'type', 'name', 'slug', 'image', 'short_description', 'description',
+        'predecessor', 'type', 'name', 'slug', 'image', 'logo', 'short_description', 'description',
         'email', 'website', 'hosts', 'owners', 'category', 'rtrcategory', 'topic',
-        'musicfocus',
+        'musicfocus', 'fallback_pool', 'cba_series_id',
     )
 
     class Media:
@@ -221,6 +226,11 @@ class ShowAdmin(admin.ModelAdmin):
         if request.POST.get('step') == None:
             # First save-show submit
 
+            # Generate thumbnails
+            if form.instance.image.name and settings.THUMBNAIL_SIZES:
+                for size in settings.THUMBNAIL_SIZES:
+                    thumbnail = form.instance.image.crop[size].name
+
             # Save show data only
             form.save();
 
diff --git a/program/migrations/0012_auto_20171108_1846.py b/program/migrations/0012_auto_20171109_1843.py
similarity index 85%
rename from program/migrations/0012_auto_20171108_1846.py
rename to program/migrations/0012_auto_20171109_1843.py
index eddb203e055017af3ebac34fe0889fb23e43491e..27c9511380358f52d1e00163fae86b05ba9fb2dc 100644
--- a/program/migrations/0012_auto_20171108_1846.py
+++ b/program/migrations/0012_auto_20171109_1843.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11.3 on 2017-11-08 18:46
+# Generated by Django 1.11.3 on 2017-11-09 18:43
 from __future__ import unicode_literals
 
 from django.conf import settings
 from django.db import migrations, models
 import django.db.models.deletion
 import tinymce.models
+import versatileimagefield.fields
 
 
 class Migration(migrations.Migration):
@@ -63,8 +64,8 @@ class Migration(migrations.Migration):
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
             ],
             options={
-                'verbose_name': 'Schedule',
-                'verbose_name_plural': 'Schedules',
+                'verbose_name': 'Program slot',
+                'verbose_name_plural': 'Program slots',
                 'ordering': ('dstart', 'tstart'),
             },
         ),
@@ -142,10 +143,20 @@ class Migration(migrations.Migration):
             name='cba_id',
             field=models.IntegerField(blank=True, null=True, verbose_name='CBA ID'),
         ),
+        migrations.AddField(
+            model_name='note',
+            name='height',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Height'),
+        ),
         migrations.AddField(
             model_name='note',
             name='image',
-            field=models.ImageField(blank=True, null=True, upload_to='note_images', verbose_name='Featured image'),
+            field=versatileimagefield.fields.VersatileImageField(blank=True, height_field='height', null=True, upload_to='note_images', verbose_name='Featured image', width_field='width'),
+        ),
+        migrations.AddField(
+            model_name='note',
+            name='ppoi',
+            field=versatileimagefield.fields.PPOIField(default='0.5x0.5', editable=False, max_length=20, verbose_name='Image PPOI'),
         ),
         migrations.AddField(
             model_name='note',
@@ -163,6 +174,11 @@ class Migration(migrations.Migration):
             name='user',
             field=models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='users', to=settings.AUTH_USER_MODEL),
         ),
+        migrations.AddField(
+            model_name='note',
+            name='width',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Width'),
+        ),
         migrations.AddField(
             model_name='show',
             name='cba_series_id',
@@ -173,11 +189,26 @@ class Migration(migrations.Migration):
             name='fallback_pool',
             field=models.CharField(blank=True, max_length=255, verbose_name='Fallback Pool'),
         ),
+        migrations.AddField(
+            model_name='show',
+            name='height',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Height'),
+        ),
         migrations.AddField(
             model_name='show',
             name='logo',
             field=models.ImageField(blank=True, null=True, upload_to='show_images', verbose_name='Logo'),
         ),
+        migrations.AddField(
+            model_name='show',
+            name='ppoi',
+            field=versatileimagefield.fields.PPOIField(default='0.5x0.5', editable=False, max_length=20, verbose_name='Image PPOI'),
+        ),
+        migrations.AddField(
+            model_name='show',
+            name='width',
+            field=models.PositiveIntegerField(blank=True, editable=False, null=True, verbose_name='Image Width'),
+        ),
         migrations.AlterField(
             model_name='musicfocus',
             name='big_button',
@@ -196,7 +227,7 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='show',
             name='image',
-            field=models.ImageField(blank=True, null=True, upload_to='show_images', verbose_name='Image'),
+            field=versatileimagefield.fields.VersatileImageField(blank=True, height_field='height', null=True, upload_to='show_images', verbose_name='Image', width_field='width'),
         ),
         migrations.AlterField(
             model_name='timeslot',
diff --git a/program/models.py b/program/models.py
index 8404731a862060fa0d627cda986e814355293be1..5835db6fb728dbe3de63111536cdcd0f007c4f30 100644
--- a/program/models.py
+++ b/program/models.py
@@ -4,6 +4,8 @@ from django.urls import reverse
 from django.db import models
 from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
+from versatileimagefield.fields import VersatileImageField, PPOIField
+from django.conf import settings
 
 from tinymce import models as tinymce_models
 
@@ -255,7 +257,10 @@ class Show(models.Model):
     musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus"))
     name = models.CharField(_("Name"), max_length=255)
     slug = models.CharField(_("Slug"), max_length=255, unique=True)
-    image = models.ImageField(_("Image"), blank=True, null=True, upload_to='show_images')
+    ppoi = PPOIField('Image PPOI')
+    height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
+    width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
+    image = VersatileImageField(_("Image"), blank=True, null=True, upload_to='show_images', width_field='width', height_field='height', ppoi_field='ppoi')
     logo = models.ImageField(_("Logo"), blank=True, null=True, upload_to='show_images')
     short_description = models.CharField(_("Short description"), max_length=64)
     description = tinymce_models.HTMLField(_("Description"), blank=True, null=True)
@@ -333,7 +338,8 @@ class Schedule(models.Model):
     tend = models.TimeField(_("End time"))
     until = models.DateField(_("Last date"))
     is_repetition = models.BooleanField(_("Is repetition"), default=False)
-    automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices())
+    fallback_playlist = models.IntegerField(_("Fallback Playlist"), blank=True, null=True)
+    automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices()) # Deprecated
     created = models.DateTimeField(auto_now_add=True, editable=False, null=True) #-> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add
     last_updated = models.DateTimeField(auto_now=True, editable=False, null=True)
 
@@ -582,7 +588,10 @@ class Note(models.Model):
     slug = models.SlugField(_("Slug"), max_length=32, unique=True)
     summary = tinymce_models.HTMLField(_("Summary"), blank=True)
     content = tinymce_models.HTMLField(_("Content"))
-    image = models.ImageField(_("Featured image"), blank=True, null=True, upload_to='note_images')
+    ppoi = PPOIField('Image PPOI')
+    height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
+    width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
+    image = VersatileImageField(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width', height_field='height', ppoi_field='ppoi')
     status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1)
     start = models.DateTimeField(editable=False)
     show = models.ForeignKey(Show, editable=False, related_name='notes')
@@ -603,4 +612,9 @@ class Note(models.Model):
         self.start = self.timeslot.start
         self.show = self.timeslot.schedule.show
 
-        super(Note, self).save(*args, **kwargs)
\ No newline at end of file
+        super(Note, self).save(*args, **kwargs)
+
+        # Generate thumbnails
+        if self.image.name and settings.THUMBNAIL_SIZES:
+            for size in settings.THUMBNAIL_SIZES:
+                thumbnail = self.image.crop[size].name
\ No newline at end of file
diff --git a/pv/settings.py b/pv/settings.py
index 6a6c2515378e199488c6c1131170e6737ea45b06..654c0caea608971ee26c9714bbab190450c412d0 100644
--- a/pv/settings.py
+++ b/pv/settings.py
@@ -87,9 +87,13 @@ INSTALLED_APPS = (
     'django.contrib.staticfiles',
     'program',
     'nop',
+    'profile',
     'tinymce',
+    'versatileimagefield',
 )
 
+THUMBNAIL_SIZES = ['200x200', '150x150']
+
 TINYMCE_JS_URL = '/static/js/tiny_mce/tiny_mce.js'
 TINYMCE_DEFAULT_CONFIG = {
     'plugins': 'contextmenu',
diff --git a/requirements.txt b/requirements.txt
index 549fc0316ff60921a5bc4bddfce536c7362d5004..1a32e6057c7de8d2891e470f589a34ff2cff9d60 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,7 @@
 Django==1.11.3
-MySQL-python==1.2.5
+mysqlclient
 Pillow==4.2.1
 PyYAML==3.12
 django-tinymce==2.6.0
 python-dateutil==2.6.0
+django-versatileimagefield==1.8.1
\ No newline at end of file