diff --git a/manage.sh b/manage.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3ab4863692d4880f6e2f1302d824dc5726e27782
--- /dev/null
+++ b/manage.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+BASE_D=$(realpath "${BASH_SOURCE%/*}/")
+
+exec sudo docker run --rm -it -u $UID:$GID -p 127.0.0.1:8000:8000 -v "$BASE_D":/srv aura/pv /srv/manage.py $@
diff --git a/program/admin.py b/program/admin.py
index 9db5ccc8699871a6202631ae83bbf73adf49fdf2..f69de36fce98db1eaf58d494a5e4fe29a9080061 100644
--- a/program/admin.py
+++ b/program/admin.py
@@ -224,14 +224,14 @@ class ShowAdmin(admin.ModelAdmin):
     filter_horizontal = ('hosts', 'owners', 'musicfocus', 'category', 'topic', 'language')
     inlines = (ScheduleInline,)
     list_display = ('name', 'short_description')
-    list_filter = (ActiveShowsFilter, 'type', 'category', 'topic', 'musicfocus', 'language', 'fundingcategory')
+    list_filter = (ActiveShowsFilter, 'type', 'category', 'topic', 'musicfocus', 'language', 'fundingcategory', 'is_public')
     ordering = ('slug',)
     prepopulated_fields = {'slug': ('name',)}
     search_fields = ('name', 'short_description', 'description')
     fields = (
         'predecessor', 'type', 'name', 'slug', 'image', 'logo', 'short_description', 'description',
         'email', 'website', 'hosts', 'owners', 'language', 'category', 'fundingcategory', 'topic',
-        'musicfocus', 'fallback_id', 'cba_series_id', 'is_active'
+        'musicfocus', 'fallback_id', 'cba_series_id', 'is_active', 'is_public'
     )
 
 
@@ -598,4 +598,4 @@ admin.site.register(Host, HostAdmin)
 admin.site.register(Note, NoteAdmin)
 admin.site.register(Schedule, ScheduleAdmin)
 admin.site.register(TimeSlot, TimeSlotAdmin)
-admin.site.register(Show, ShowAdmin)
\ No newline at end of file
+admin.site.register(Show, ShowAdmin)
diff --git a/program/management/commands/addnote.py b/program/management/commands/addnote.py
index 5c0147eb27451207b803144c6a7f89df1bd0eff3..fafb33fcc3deb637bdfd1ec6d0430100dab8be9a 100644
--- a/program/management/commands/addnote.py
+++ b/program/management/commands/addnote.py
@@ -64,4 +64,4 @@ class Command(BaseCommand):
             raise CommandError(ve.messages[0])
         else:
             note.save()
-            print 'added note "%s" to "%s"' % (title, timeslot)
+            self.stdout.write(self.style.SUCCESS, f'added note "{title}" to "{timeslot}"')
diff --git a/program/management/commands/check_automation_ids.py b/program/management/commands/check_automation_ids.py
index 8bfe164391306f3b726dfbad648335e8bb7c1e22..825149ea988aad3ddb0f811aa61b5337ae7798e8 100644
--- a/program/management/commands/check_automation_ids.py
+++ b/program/management/commands/check_automation_ids.py
@@ -35,10 +35,10 @@ class Command(NoArgsCommand):
                     multi_id = rd_ids[automation_id]['multi']['id']
                 if automation_id not in pv_ids and multi_id not in pv_ids:
                     if multi_id < 0:
-                        print '+ %d' % (automation_id)
+                        self.stdout.write(self.style.NOTICE, f'+ {automation_id}')
                     else:
-                        print '+ %d (%d)' % (automation_id, multi_id)
+                        self.stdout.write(self.style.NOTICE, f'+ {automation_id} ({multi_id})')
 
             for automation_id in sorted(pv_ids):
                 if automation_id not in rd_ids:
-                    print '-', automation_id
\ No newline at end of file
+                    self.stdout.write(self.style.NOTICE, f'- {automation_id}')
diff --git a/program/management/commands/createuser.py b/program/management/commands/createuser.py
index a78c1014e0182cdfc7d6b55c0638d2f07376addc..422c4bff8ebdb76f3e567ba148d862119a74df39 100644
--- a/program/management/commands/createuser.py
+++ b/program/management/commands/createuser.py
@@ -21,6 +21,6 @@ class Command(BaseCommand):
             User.objects.get(username=username)
         except User.DoesNotExist:
             User.objects.create_user(username=username, email=email)
-            print 'user created successfully.'
+            self.stdout.write(self.style.SUCCESS, 'user created successfully.')
         else:
-            print 'User already exists, no need to create.'
+            self.stdout.write(self.style.NOTICE, 'User already exists, no need to create.')
diff --git a/program/management/commands/deleteuser.py b/program/management/commands/deleteuser.py
index db2cb60b6e5f79654c2f9a894d3cb91ba4e3bce1..a8bb2308ec7c401c92722e85ddc4750e00469bdc 100644
--- a/program/management/commands/deleteuser.py
+++ b/program/management/commands/deleteuser.py
@@ -20,4 +20,4 @@ class Command(BaseCommand):
         except User.DoesNotExist:
             raise 'user does not exist.'
         else:
-            print 'user deleted succesfuly.'
+            self.stdout.write(self.style.SUCCESS, 'user deleted succesfuly.')
diff --git a/program/management/commands/export_showlog.py b/program/management/commands/export_showlog.py
index 0e26c011941df2978747289caf7d6664eda9a079..41c202178d8e3a0b1e3df0ba30ca8bfe868c3be9 100644
--- a/program/management/commands/export_showlog.py
+++ b/program/management/commands/export_showlog.py
@@ -2,7 +2,7 @@
 
 import codecs
 import sys
-from datetime import date, datetime, time, timedelta
+from datetime import datetime
 from django.core.management.base import BaseCommand, CommandError
 from program.models import TimeSlot
 
@@ -23,21 +23,21 @@ class Command(BaseCommand):
         else:
             raise CommandError('you must provide the year')
 
-        print "# Radio Helsinki Sendungslog %d" % year
+        self.stdout.write(self.style.NOTICE, f"# Radio Helsinki Sendungslog {year}")
 
         start = datetime.strptime('%d__01__01__00__00' % (year), '%Y__%m__%d__%H__%M')
         end = datetime.strptime('%d__01__01__00__00' % (year+1), '%Y__%m__%d__%H__%M')
 
         currentDate = None
         for ts in TimeSlot.objects.filter(end__gt=start, start__lt=end).select_related('schedule').select_related('show'):
-            if currentDate == None or currentDate < ts.start.date():
+            if currentDate is None or currentDate < ts.start.date():
                 if currentDate:
-                    print "\n"
+                    self.stdout.write('\n')
                 currentDate = ts.start.date()
-                print currentDate.strftime("## %a %d.%m.%Y:\n")
+                self.stdout.write(self.style.NOTICE, currentDate.strftime("## %a %d.%m.%Y:\n"))
 
             title = ts.show.name
             if ts.schedule.is_repetition:
                 title += " (WH)"
 
-            print " * **%s - %s**: %s" % (ts.start.strftime("%H:%M:%S"), ts.end.strftime("%H:%M:%S"), title)
\ No newline at end of file
+            self.stdout.write(self.style.NOTICE, f' * **{ts.start.strftime("%H:%M:%S")} - {ts.end.strftime("%H:%M:%S")}**: {title}')
diff --git a/program/management/commands/importhosts.py b/program/management/commands/importhosts.py
index 31f4a3f61be828408214258c3246d168803c3318..a0b92923d7f722aaa888edddd8528706dcf82b1e 100644
--- a/program/management/commands/importhosts.py
+++ b/program/management/commands/importhosts.py
@@ -32,4 +32,4 @@ WHERE letzter_termin > current_date AND macher != '' AND titel NOT LIKE 'Musikpr
         cursor.close()
         connection.close()
 
-        print '%i hosts imported' % counter
+        self.stdout.write(self.style.SUCCESS, F'{counter} hosts imported')
diff --git a/program/management/commands/importnotes.py b/program/management/commands/importnotes.py
index f835e37ed085a64b3c7f28ca2a81640acdc5e7e7..91476e6f9e094fedda2a5bd1f092ab57aae06531 100644
--- a/program/management/commands/importnotes.py
+++ b/program/management/commands/importnotes.py
@@ -36,30 +36,30 @@ WHERE n.sendung_id in (SELECT id FROM sendungen WHERE letzter_termin > current_d
                 try:
                     show = Show.objects.get(name=stitel)
                 except ObjectDoesNotExist:
-                    print 'show with name "%s" not found' % stitel
+                    self.stdout.write(self.style.WARNING, f'show with name "{stitel}" not found')
                 else:
                     try:
                         timeslot = TimeSlot.objects.get(schedule__show=show, start__year=year, start__month=month,
                                                         start__day=day)
                     except ObjectDoesNotExist:
-                        print 'no timeslot found for sendung "%s" and datum "%s"' % (stitel, datum)
+                        self.stdout.write(self.style.WARNING, f'no timeslot found for sendung "{stitel}" and datum "{datum}"')
                     except MultipleObjectsReturned:
-                        print 'multiple timeslots found for sendung "%s" and datum "%s"' % (stitel, datum)
+                        self.stdout.write(self.style.WARNING, f'multiple timeslots found for sendung "{stitel}" and datum "{datum}"')
                     else:
                         note = Note(timeslot=timeslot, title=ntitel, content=notiz)
                         try:
                             note.validate_unique()
                         except ValidationError:
-                            print 'note already imported for show "%s" and datum "%s"' % (stitel, datum)
+                            self.stdout.write(self.style.WARNING, f'note already imported for show "{stitel}" and datum "{datum}"')
                         else:
                             try:
                                 note.save()
                             except:
-                                print 'could not save note "%s" for show "%s" and datum "%s"' % (ntitel, stitel, datum)
+                                self.stdout.write(self.style.ERROR, f'could not save note "{ntitel}" for show "{stitel}" and datum "{datum}"')
                             else:
                                 counter += 1
 
         cursor.close()
         connection.close()
 
-        print '%i notes imported' % counter
\ No newline at end of file
+        self.stdout.write(self.style.SUCCESS, f'{counter} notes imported')
diff --git a/program/management/commands/importprogramslots.py b/program/management/commands/importprogramslots.py
index b6e542279b1cdd7f87df155181e93a31959b0912..8ce8a2bdd80084202bc75e37feedfff5098fea47 100644
--- a/program/management/commands/importprogramslots.py
+++ b/program/management/commands/importprogramslots.py
@@ -48,17 +48,17 @@ WHERE letzter_termin > current_date AND titel NOT LIKE 'Musikprogramm' AND titel
                 try:
                     show = Show.objects.get(name=titel)
                 except ObjectDoesNotExist:
-                    print 'show with name "%s" not found' % titel
+                    self.stdout.write(self.style.NOTICE, f'show with name "{titel}" not found')
                 else:
                     schedule = Schedule(rrule=rrule, byweekday=termin, show=show, dstart=erster_termin,
-                                              tstart=tstart, tend=tend, until=letzter_termin)
+                                        tstart=tstart, tend=tend, until=letzter_termin)
                     try:
                         schedule.save()
                         counter += 1
                     except:
                         pass
             except KeyError:
-                print 'rhythmus "%i" is not supported for sendung "%s"' % (rhytmus, titel)
+                self.stdout.write(self.style.NOTICE, f'rhythmus "{rhytmus}" is not supported for sendung "{titel}"')
 
         cursor.execute("""SELECT titel, beginn, ende, erster_termin, letzter_termin, rhytmus, termin
 FROM sendungen
@@ -80,19 +80,19 @@ WHERE letzter_termin > current_date AND titel LIKE '%%(Wiederholung)'""")
                 try:
                     show = Show.objects.get(name=titel)
                 except ObjectDoesNotExist:
-                    print 'show with name "%s" not found' % titel
+                    self.stdout.write(self.style.WARNING, f'show with name "{titel}" not found')
                 else:
                     schedule = Schedule(rrule=rrule, byweekday=termin, show=show, dstart=erster_termin,
-                                              tstart=tstart, tend=tend, until=letzter_termin, is_repetition=True)
+                                        tstart=tstart, tend=tend, until=letzter_termin, is_repetition=True)
                     try:
                         schedule.save()
                         counter += 1
                     except:
                         pass
             except KeyError:
-                print 'rhythmus "%i" is not supported for sendung "%s"' % (rhytmus, titel)
+                self.stdout.write(self.style.WARNING, f'rhythmus "{rhytmus}" is not supported for sendung "{titel}"')
 
         cursor.close()
         connection.close()
 
-        print '%i schedules imported' % counter
\ No newline at end of file
+        self.stdout.write(self.style.SUCCESS, f'{counter} schedules imported')
diff --git a/program/management/commands/importshows.py b/program/management/commands/importshows.py
index f1353b8a88cd2d9e39a586cddb309fb848d5f8db..cff3d434d8b29ab7692c1da57e2cb93292020e27 100644
--- a/program/management/commands/importshows.py
+++ b/program/management/commands/importshows.py
@@ -41,15 +41,15 @@ ORDER BY titel, beginn, ende""")
                 try:
                     host = Host.objects.get(name=macher)
                 except MultipleObjectsReturned:
-                    print 'multiple hosts with name "%s" found' % macher
+                    self.stdout.write(self.style.NOTICE, f'multiple hosts with name "{macher}" found')
                 except ObjectDoesNotExist:
-                    print 'host with name "%s" not found' % macher
+                    self.stdout.write(self.style.NOTICE, f'host with name "{macher}" not found')
                 else:
                     hosts.append(host)
 
             try:
                 show = Show.objects.get(name=titel)
-                print 'sendung "%s" already imported as show "%s"' % (titel, show)
+                self.stdout.write(self.style.NOTICE, f'sendung "{titel}" already imported as show "{show}"')
             except ObjectDoesNotExist:
                 show = Show(broadcastformat=TALK, name=titel, slug=slug, short_description='FIXME',
                             description=beschreibung)
@@ -57,7 +57,7 @@ ORDER BY titel, beginn, ende""")
                     show.save()
                     counter += 1
                 except:
-                    print 'sendung "%s" could not be imported' % titel
+                    self.stdout.write(self.style.NOTICE, f'sendung "{titel}" could not be imported')
                 else:
                     for h in hosts:
                         show.hosts.add(h)
@@ -66,4 +66,4 @@ ORDER BY titel, beginn, ende""")
         cursor.close()
         connection.close()
 
-        print '%i shows imported' % counter
+        self.stdout.write(self.style.SUCCESS, f'{counter} shows imported')
diff --git a/program/migrations/0022_show_is_public.py b/program/migrations/0022_show_is_public.py
new file mode 100644
index 0000000000000000000000000000000000000000..51136dd18633f0b41edaf94c37f6f39862f31954
--- /dev/null
+++ b/program/migrations/0022_show_is_public.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.3 on 2019-09-18 12:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('program', '0021_show_is_active'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='show',
+            name='is_public',
+            field=models.BooleanField(default=False, help_text='Files and Playlists of Public Shows can only be changed by owners but may be used by everyone.', verbose_name='Is Public?'),
+        ),
+    ]
diff --git a/program/models.py b/program/models.py
index c56e6708361ce74f0c4565fa3bb2aa5b45645c90..ae330a3d9bb28b5b2ecc3321a44434eb8efc3678 100644
--- a/program/models.py
+++ b/program/models.py
@@ -325,6 +325,7 @@ class Show(models.Model):
     created = models.DateTimeField(auto_now_add=True, editable=False)
     last_updated = models.DateTimeField(auto_now=True, editable=False)
     is_active = models.BooleanField(_("Is active?"), default=True)
+    is_public = models.BooleanField(_("Is Public?"), default=False, help_text=_("Files and Playlists of Public Shows can only be changed by owners but may be used by everyone."))
 
     class Meta:
         ordering = ('slug',)
diff --git a/program/serializers.py b/program/serializers.py
index 779316f4100e78f65b42f3cbd7ea7367c2ab7509..6f6c5c3a88e1cb27016c0b4771e81553034e27e2 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -256,7 +256,7 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
         fields = ('id', 'name', 'slug', 'image', 'ppoi', 'logo', 'short_description', 'description',
                   'email', 'website', 'created', 'last_updated', 'type', 'fundingcategory',
                   'predecessor', 'cba_series_id', 'fallback_id', 'category', 'hosts',
-                  'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active')
+                  'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active', 'is_public')
 
 
     def create(self, validated_data):
@@ -316,6 +316,7 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
             instance.fundingcategory = validated_data.get('fundingcategory', instance.fundingcategory)
             instance.predecessor = validated_data.get('predecessor', instance.predecessor)
             instance.is_active = validated_data.get('is_active', instance.is_active)
+            instance.is_public = validated_data.get('is_public', instance.is_public)
 
         instance.save()
         return instance
diff --git a/program/views.py b/program/views.py
index 4b139d16d36d654812dc352a0958c584c84e70b0..48a7c45c132a5f1472ea0db42a22fbc4eb5ba3ab 100644
--- a/program/views.py
+++ b/program/views.py
@@ -443,6 +443,8 @@ class APIShowViewSet(viewsets.ModelViewSet):
     /api/v1/shows/                                             Returns all shows (GET, POST)
     /api/v1/shows/?active=true                                 Returns all active shows (= currently running) (GET)
     /api/v1/shows/?active=false                                Returns all inactive shows (= past or upcoming) (GET)
+    /api/v1/shows/?public=true                                 Returns all public shows (GET)
+    /api/v1/shows/?public=false                                Returns all non-public shows (GET)
     /api/v1/shows/?host=1                                      Returns shows assigned to a given host (GET)
     /api/v1/shows/?owner=1                                     Returns shows of a given owner (GET)
     /api/v1/shows/1                                            Used for retrieving a single show or update (if owned) (GET, PUT) - DELETE is not allowed via API. Set is_active to False instead.
@@ -489,6 +491,14 @@ class APIShowViewSet(viewsets.ModelViewSet):
             '''Return all shows except those which are running'''
             shows = Show.objects.exclude(id__in=show_ids,is_active=True)
 
+        if self.request.GET.get('public') == 'true':
+            '''Return all public shows'''
+            shows = shows.filter(is_public=True)
+
+        if self.request.GET.get('public') == 'false':
+            '''Return all public shows'''
+            shows = shows.filter(is_public=False)
+
         if self.request.GET.get('owner') != None:
             '''Filter shows by owner'''
             shows = shows.filter(owners__in=[int(self.request.GET.get('owner'))])
diff --git a/pv/oidc_provider_settings.py b/pv/oidc_provider_settings.py
index e1b96998b883d69f3925736daa66325963297471..a3e2465b3ee6607f9153dc4574058938d15147b7 100644
--- a/pv/oidc_provider_settings.py
+++ b/pv/oidc_provider_settings.py
@@ -11,7 +11,9 @@ class AuraScopeClaims(ScopeClaims):
 
     def scope_username(self):
         dic = {
-            'username': self.user.username
+            'username': self.user.username,
+            #'privileged': (self.user.is_staff or self.user.is_superuser)
+            'privileged': (self.user.is_superuser)
         }
 
         return dic
@@ -22,9 +24,14 @@ class AuraScopeClaims(ScopeClaims):
     )
 
     def scope_aura_shows(self):
+        from program.models import Show
+
+        # TODO: should add filter `is_active=True` ?
+        public_show_slugs = list(Show.objects.filter(is_public=True).values_list('slug', flat=True))
         show_slugs = list(self.user.shows.all().values_list('slug', flat=True))
         dic = {
-            'shows': show_slugs
+            'shows': show_slugs,
+            'public-shows': public_show_slugs
         }
 
         return dic