diff --git a/profile/admin.py b/profile/admin.py index 6fffd8e3fef000b0a7401ceecc7a75964ed0049a..7f8385ce1e8bbeb68832be98fbed9d8f2f03e504 100644 --- a/profile/admin.py +++ b/profile/admin.py @@ -10,19 +10,29 @@ class ProfileInline(admin.StackedInline): verbose_name_plural = 'Profile' fk_name = 'user' + class ProfileUserAdmin(UserAdmin): inlines = (ProfileInline, ) + def get_queryset(self, request): - # Users can only edit their own profile + '''Let common users only edit their own profile''' if not request.user.is_superuser: return super(UserAdmin, self).get_queryset(request).filter(pk=request.user.id) return super(UserAdmin, self).get_queryset(request) + def get_readonly_fields(self, request, obj=None): + '''Limit field access for common users''' + if not request.user.is_superuser: + return ('username', 'is_staff', 'is_superuser', 'is_active', 'date_joined', 'last_login', 'groups', 'user_permissions') + return list() + def get_inline_instances(self, request, obj=None): + '''Append profile fields to UserAdmin''' if not obj: return list() + return super(ProfileUserAdmin, self).get_inline_instances(request, obj) admin.site.unregister(User) diff --git a/profile/models.py b/profile/models.py index 40a04a8668ddca8cade034ebb1ec5bf8c7e70251..a02b556b0fd63ca0346f5bbf800fda9b1ba7fd48 100644 --- a/profile/models.py +++ b/profile/models.py @@ -12,21 +12,21 @@ 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) + biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True, help_text=_("Describe yourself and your fields of interest in a few sentences.")) + website = models.URLField(_("Website"), blank=True, help_text=_("URL to your personal website.")) + googleplus_url = models.URLField(_("Google+ URL"), blank=True, help_text=_("URL to your Google+ profile.")) + facebook_url = models.URLField(_("Facebook URL"), blank=True, help_text=_("URL to your Facebook profile.")) + twitter_url = models.URLField(_("Twitter URL"), blank=True, help_text=_("URL to your Twitter profile.")) + linkedin_url = models.URLField(_("LinkedIn URL"), blank=True, help_text=_("URL to your LinkedIn profile.")) + youtube_url = models.URLField(_("Youtube URL"), blank=True, help_text=_("URL to your Youtube channel.")) + dorftv_url = models.URLField(_("DorfTV URL"), blank=True, help_text=_("URL to your dorfTV channel.")) + cba_url = models.URLField(_("CBA URL"), blank=True, help_text=_("URL to your CBA profile.")) + cba_username = models.CharField(_("CBA Username"), blank=True, max_length=60, help_text=_("Your username in CBA. This is necessary for uploading files to your account.")) + cba_user_token = models.CharField(_("CBA Token"), blank=True, max_length=255, help_text=_("The CBA upload token for your account. This is NOT your password which you use to log into CBA!")) 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') + image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='user_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) def __str__(self): return self.user.username diff --git a/program/admin.py b/program/admin.py index ab7f53b791225fe14763be7c2a63e6e56f2e8b22..4bcfe9d4f36d821831002c47d18d212f2d6c2458 100644 --- a/program/admin.py +++ b/program/admin.py @@ -5,7 +5,7 @@ from django.shortcuts import render from django.conf import settings from .models import Language, Type, MusicFocus, Category, Topic, RTRCategory, Host, Note, RRule, Schedule, Show, TimeSlot -from .forms import MusicFocusForm, CollisionForm +from .forms import MusicFocusForm from datetime import date, datetime, time, timedelta @@ -202,6 +202,14 @@ class ShowAdmin(admin.ModelAdmin): 'musicfocus', 'fallback_pool', 'cba_series_id', ) + + class Media: + js = [ settings.MEDIA_URL + 'js/calendar/lib/moment.min.js', + settings.MEDIA_URL + 'js/show_change.js', ] + + css = { 'all': ('/program/styles.css',) } + + def get_queryset(self, request): if request.user.is_superuser: # Superusers see all shows @@ -212,11 +220,12 @@ class ShowAdmin(admin.ModelAdmin): return super(ShowAdmin, self).get_queryset(request).filter(pk__in=shows) - class Media: - js = [ settings.MEDIA_URL + 'js/calendar/lib/moment.min.js', - settings.MEDIA_URL + 'js/show_change.js', ] - css = { 'all': ('/program/styles.css',) } + def get_readonly_fields(self, request, obj=None): + '''Limit field access for common users''' + if not request.user.is_superuser: + return ('predecessor', 'type', 'hosts', 'owners', 'language', 'category', 'topic', 'musicfocus', 'rtrcategory') + return list() def formfield_for_foreignkey(self, db_field, request=None, **kwargs): @@ -490,13 +499,16 @@ class ShowAdmin(admin.ModelAdmin): Displays the collision form for the current schedule otherwise """ + # Never check for collisions if not superuser + # Common users can't edit the formset, so save_formset() will never be called thus end_reached wasn't set yet + if not request.user.is_superuser: + self.end_reached = True + if self.end_reached: return super(ShowAdmin, self).response_change(request, obj) timeslots_to_collisions = list(zip(self.timeslots, self.collisions)) - # myform = CollisionForm(self.timeslots, self.collisions) - return render(request, 'collisions.html', {'self' : self, 'obj': obj, 'request': request, 'timeslots': self.timeslots, 'collisions': self.collisions, diff --git a/program/forms.py b/program/forms.py index 28f04f1c0c3e7bba1a269aece9a665e326e1fb5e..d84d1b2bf74af8dc09444d9600d31e54fb48c9f5 100644 --- a/program/forms.py +++ b/program/forms.py @@ -5,16 +5,6 @@ from django.core.files.images import get_image_dimensions from program.models import MusicFocus, Category, Topic -# Couldn't manage call/validation for collision usecase. -class CollisionForm(forms.Form): - - def __init__(self, timeslots, collisions, *args, **kwargs): - super(CollisionForm, self).__init__(*args, **kwargs) - - for i in range(min(len(timeslots), len(timeslots))): - self.fields['resolved_' + str(i)] = forms.ChoiceField(widget=forms.RadioSelect,choices=((timeslots[i], timeslots[i]),(collisions[i], collisions[i]))) - - class FormWithButton(ModelForm): def clean_button(self): button = self.cleaned_data.get('button') diff --git a/program/models.py b/program/models.py index 9da8178c1a26e2c824dc839640e1a6bd271fcffa..a5f8b494d887fe48b48959ddba800a43cb8cdb78 100644 --- a/program/models.py +++ b/program/models.py @@ -268,18 +268,18 @@ class Show(models.Model): rtrcategory = models.ForeignKey(RTRCategory, related_name='shows', verbose_name=_("RTR Category")) topic = models.ManyToManyField(Topic, blank=True, related_name='shows', verbose_name=_("Topic")) 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) + name = models.CharField(_("Name"), max_length=255, help_text=_("The show's name. Avoid a subtitle.")) + slug = models.CharField(_("Slug"), max_length=255, unique=True, help_text=_("A simple to read URL for your show")) 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') + image = VersatileImageField(_("Image"), blank=True, null=True, upload_to='show_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your show. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) 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) - email = models.EmailField(_("E-Mail"), blank=True, null=True) - website = models.URLField(_("Website"), blank=True, null=True) - cba_series_id = models.IntegerField(_("CBA Series ID"), blank=True, null=True) + short_description = models.TextField(_("Short description"), help_text=_("Describe your show in some sentences. Avoid technical data like airing times and contact information. They will be added automatically.")) + description = tinymce_models.HTMLField(_("Description"), blank=True, null=True, help_text=_("Describe your show in detail.")) + email = models.EmailField(_("E-Mail"), blank=True, null=True, help_text=_("The main contact email address for your show.")) + website = models.URLField(_("Website"), blank=True, null=True, help_text=_("Is there a website to your show? Type in its URL.")) + cba_series_id = models.IntegerField(_("CBA Series ID"), blank=True, null=True, help_text=_("Link your show to a CBA series by giving its ID. This will enable CBA upload and will automatically link your show to your CBA archive. Find out your ID under https://cba.fro.at/series")) fallback_pool = models.CharField(_("Fallback Pool"), max_length=255, blank=True) created = models.DateTimeField(auto_now_add=True, editable=False) last_updated = models.DateTimeField(auto_now=True, editable=False) @@ -602,18 +602,18 @@ class Note(models.Model): (2, _("Repetition")), ) timeslot = models.OneToOneField(TimeSlot, verbose_name=_("Time slot"), unique=True) - title = models.CharField(_("Title"), max_length=128) - slug = models.SlugField(_("Slug"), max_length=32, unique=True) - summary = tinymce_models.HTMLField(_("Summary"), blank=True) - content = tinymce_models.HTMLField(_("Content")) + title = models.CharField(_("Title"), max_length=128, help_text=_("Give your note a good headline. What will your upcoming show be about? Try to arouse interest to listen to it!<br>Avoid technical data like the show's name, its airing times or its episode number. These data are added automatically.")) + slug = models.SlugField(_("Slug"), max_length=32, unique=True, help_text=_("A simple to read URL for your show.")) + summary = tinymce_models.HTMLField(_("Summary"), blank=True, help_text=_("Describe your upcoming show in some sentences. Avoid technical data like airing times and contact information. They will be added automatically.")) + content = tinymce_models.HTMLField(_("Content"), help_text=_("Describe your upcoming show in detail.")) 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') + image = VersatileImageField(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your show. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1) start = models.DateTimeField(editable=False) - show = models.ForeignKey(Show, related_name='notes', editable=True) # User chooses the show. Timeslots are loaded via ajax. - cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True) + show = models.ForeignKey(Show, related_name='notes', editable=True) + cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True, help_text=_("Link the note to a certain CBA post by giving its ID. (E.g. if your post's CBA URL is https://cba.fro.at/1234, then your CBA ID is 1234)")) created = models.DateTimeField(auto_now_add=True, editable=False) last_updated = models.DateTimeField(auto_now=True, editable=False) user = models.ForeignKey(User, editable=False, related_name='users', default=1)