Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Photomanagement #244

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
37 changes: 28 additions & 9 deletions wbcore/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ def formfield_for_manytomany(self, db_field, request, **kwargs):
# making the field readonly
kwargs['disabled'] = True
kwargs["queryset"] = request.user.hosts

elif db_field.name == 'photos':
hosts = request.user.get_hosts_for_role(['admin', 'editor', 'author'])
recent_photos = Photo.objects.filter(host__in=hosts).all().order_by('-date_added')[:100]
kwargs["queryset"] = Photo.objects.filter(id__in=recent_photos).order_by('-date_added')

return super().formfield_for_manytomany(db_field, request, **kwargs)

def get_exclude(self, request, obj=None):
Expand Down Expand Up @@ -242,13 +248,18 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs):
# making the field readonly
kwargs['disabled'] = True
else:
hosts = request.user.get_hosts_for_admin()
hosts = request.user.get_hosts_for_role(['admin', 'editor', 'author'])
kwargs["queryset"] = Host.objects.filter(pk__in=[host.pk for host in hosts])

elif db_field.name == 'project':
hosts = request.user.hosts.all()
kwargs["queryset"] = Project.objects.filter(hosts__in=hosts).all()

elif db_field.name == 'image':
hosts = request.user.get_hosts_for_role(['admin', 'editor', 'author'])
recent_photos = Photo.objects.filter(host__in=hosts).all().order_by('-date_added')[:100]
kwargs["queryset"] = Photo.objects.filter(id__in=recent_photos).order_by('-date_added')

return super().formfield_for_foreignkey(db_field, request, **kwargs)

def get_queryset(self, request):
Expand Down Expand Up @@ -396,7 +407,8 @@ def queryset(self, request, queryset):
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('username', 'name', 'email', 'date_of_birth', 'get_hosts', 'get_roles',)
list_display = ('admin_thumb', 'username', 'name', 'email', 'date_of_birth', 'get_hosts', 'get_roles',)
list_display_links = ('name', 'username')
list_filter = (HostListFilter, RoleListFilter,)
fieldsets = (
('Account', {'fields': ('username', 'email', 'password')}),
Expand Down Expand Up @@ -536,13 +548,15 @@ def save_model(self, request, obj, form, change):
if obj.image != User.objects.get(pk=obj.pk).image: # delete old thumbnails on profile picture change
get_thumbnailer(User.objects.get(pk=obj.pk).image).delete_thumbnails()
except User.DoesNotExist:
pass
pass #User is created

super(UserAdmin, self).save_model(request, obj, form, change)

class TeamAdmin(MyAdmin):
inlines = (TeamUserRelationInlineModel,)

list_display = ('name', 'slug', 'host', 'get_member', 'rank')
list_display = ('admin_thumb', 'name', 'slug', 'host', 'get_member', 'rank')
list_display_links = ('name', 'admin_thumb')
prepopulated_fields = {'slug': ('name', 'host', )}

def get_member(self, team):
Expand Down Expand Up @@ -599,6 +613,7 @@ class PostAdmin(MyAdmin):

ordering = ('-published', 'title',)
search_fields = ('title', 'text')
list_display_links = ('title', 'admin_thumb')

prepopulated_fields = {'slug': ('title',)}

Expand Down Expand Up @@ -633,9 +648,9 @@ def get_list_filter(self, request):

def get_list_display(self, request):
if request.user.is_super_admin or request.user.is_superuser:
return 'title', 'get_author', 'host', 'published',
return 'admin_thumb', 'title', 'get_author', 'host', 'published',
else:
return 'title', 'get_author', 'published',
return 'admin_thumb', 'title', 'get_author', 'published',


class ContactMessageAdmin(MyAdmin):
Expand Down Expand Up @@ -673,7 +688,8 @@ class ProjectAdmin(MyAdmin, ReverseModelAdmin):
inline_type = 'stacked'
inline_reverse = ['donation_account', 'milestones']
#inlines = (MilestoneInlineModel, )
list_display = ('name', 'get_hosts', 'get_country', 'start_date', 'end_date', 'completed', 'published')
list_display = ('admin_thumb','name', 'get_hosts', 'get_country', 'start_date', 'end_date', 'completed', 'published')
list_display_links = ('name', 'admin_thumb')
prepopulated_fields = {'slug': ('name',)}
autocomplete_fields = ['hosts']

Expand All @@ -693,7 +709,8 @@ def get_country(self, project):

class EventAdmin(MyAdmin):

list_display = ('title', 'start', 'end', 'host')
list_display = ('admin_thumb', 'title', 'start', 'end', 'host')
list_display_links = ('title', 'admin_thumb')
prepopulated_fields = {'slug': ('title', 'host', )}

ordering = ('-start',)
Expand Down Expand Up @@ -807,8 +824,10 @@ def get_occurrences(self, location):

class PhotoAdmin(MyAdmin):

list_display = ('title', 'slug', 'type', 'uploader', 'host', 'admin_thumbnail')
list_display = ('admin_thumb', 'title', 'slug', 'type', 'uploader', 'host', 'date_added')
list_display_links = ('title', 'admin_thumb')
exclude = ('uploader', 'sites',)
ordering = ('-date_added',)

def save_model(self, request, obj, form, change):
if not obj.uploader:
Expand Down
36 changes: 36 additions & 0 deletions wbcore/management/commands/addhosttophotos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.core.management.base import BaseCommand
from wbcore.models import NewsPost, BlogPost
import datetime
from django.db import transaction

class Command(BaseCommand):
"""
Add the host to the imported Photos which are used in the blog and news posts.
Set the first photo of each post as title image.
To Update query is hard becuse of the many-to-many relation, thus they are save in an atomic transaction.
"""

def handle(self, *args, **options):
for Model, date in zip([NewsPost, BlogPost], [datetime.date(2019, 11, 26), datetime.date(2019, 12, 11)]):
self.addhosttomodelinstances(Model, date)

def addhosttomodelinstances(self, Model, date):
posts = Model.objects.filter(published__date__lte=date).exclude(photos=None).prefetch_related('photos')
try:
with transaction.atomic():
for post in posts:
host = post.host
for pic in post.photos.all():
if pic.host is None:
pic.host = host
pic.save()
if post.image is None:
try:
title_image = post.photos.filter(title__endswith='-1')[0]
post.image = title_image
post.save()
except IndexError:
print('No photo found for {}'.format(post.title))
except Exception as e:
print(e)
print(posts.count())
110 changes: 110 additions & 0 deletions wbcore/management/commands/convertmartorphotos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from django.core.management.base import BaseCommand
from django.core.files import File
from django.db import IntegrityError
from wbcore.models import NewsPost, BlogPost, Content, Document, Event, Partner, Project, Team, Photo
import datetime
import re
import os
from weitblick import settings
from slugify import slugify


class Command(BaseCommand):
"""
This is necessary to update the location and type of photos updated previously by the text Editor
to our new standard.
Crawl all news models (except imported news and posts) for uploaded photos in the TextFields.
Create a photologue photo object for each upload and change the corresponding url in the TextField.
"""
def handle(self, *args, **options):
modelfields = [
(NewsPost, ["text", "teaser"]),
(BlogPost, ["text", "teaser"]),
(Content, ["text"]),
(Document, ["description"]),
(Event, ["description", "teaser"]),
(Partner, ["description"]),
(Project, ["description", "short_description"]),
(Team, ["description", "teaser"])
]

for Model, fields in modelfields:
self.convertphotosofmodel(Model, fields)

def convertphotosofmodel(self, Model, fields):
objs = Model.objects.all()
if Model == NewsPost:
objs = objs.filter(published__date__gte=datetime.date(2019, 11, 26))
elif Model == BlogPost:
objs = objs.filter(published__date__gte=datetime.date(2019, 12, 11))

for obj in objs:
self.convertphotosofentry(obj, fields, Model)
print('FINISHED MODEL {}'.format(Model))


def convertphotosofentry(self, obj, fields, Model):
for field in fields:
try: # if field is translated, pictures can be uploaded for all languages
for lang in ['_de', '_en', '_es', '_fr']:
langfield = field + lang
self.updatetext(langfield, Model, obj)
except AttributeError:
self.updatetext(field, Model, obj)

def updatetext(self, fieldname, Model, obj):
text = getattr(obj, fieldname)
p = re.compile(r"(!\[([^\]]*)\]\()/(media/images/uploads/[^\!\'\"\s]*.(jpg|png|gif|jpeg|bmp|svg|webp|tif|tiff))([^\)]*\))",
re.I)
if text:
new_text = p.sub(lambda x: self.createphotologuepic(x, Model, obj), text)
if not new_text == text:
setattr(obj, fieldname, new_text)
obj.save()
print("replaced picture in {}; slug: {}; {}".format(Model, obj.slug, fieldname))

def createphotologuepic(self, match, Model, obj):
url = match.group(3)
name = match.group(2)
if Model == NewsPost:
mytype = 'news'
elif Model == BlogPost:
mytype = 'blog'
elif Model == Event:
mytype = 'event'
elif Model == Project:
mytype = 'project'
else:
mytype = None

try:
user = getattr(obj, 'author')
except AttributeError:
user = None

try:
updated_time = obj.updated
except AttributeError:
updated_time = datetime.date.today()
myname = '{} Texteditor Upload {}'.format(updated_time.strftime("%Y-%m-%d"), name)

imagepath = os.path.join(settings.ENV_PATH, url)
imageobj = File(open(imagepath, 'rb'))
imageobj.name = myname

hosts = obj.get_hosts()
if len(hosts) == 1:
host = hosts[0]
else:
host = None
if Photo.objects.filter(title=myname).exists():
new_url = Photo.objects.get(title=myname).image.url
else:
try:
photo_obj = Photo(type=mytype, uploader=user, image=imageobj, title=myname, slug=slugify(myname), host=host)
photo_obj.save()
new_url = photo_obj.image.url
except IntegrityError:
new_url = Photo.objects.get(slug=slugify(myname)).image.url # necessary if image exists twice in field
return match.group(1) + new_url + match.group(5)

43 changes: 32 additions & 11 deletions wbcore/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,36 @@
from django.db.models.query import QuerySet
from django.utils.translation import ugettext_lazy as _
import rules, datetime
from django.utils.safestring import mark_safe
from easy_thumbnails.files import get_thumbnailer
from weitblick import settings

class AdminThumbnailMixin(models.Model):
class Meta:
abstract = True

def admin_thumb(self):
try:
return self.admin_thumbnail()
except AttributeError: # if class is not Photo
if self.image:
if type(self.image) == Photo:
return self.image.admin_thumbnail()
else:
thumbnailer = get_thumbnailer(self.image)
return mark_safe('<img src="%s%s" />' % (settings.MEDIA_URL, thumbnailer['profile_list_view']))
else:
return None

admin_thumb.short_description = _('Photo')


class Photo(RulesModelMixin, PhotologuePhoto, metaclass=RulesModelBase):
class Photo(RulesModelMixin, AdminThumbnailMixin, PhotologuePhoto, metaclass=RulesModelBase):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin | pred.is_editor,
"add": pred.is_super_admin | pred.is_admin | pred.is_editor | pred.is_author,
"view": rules.always_allow,
"change": pred.is_super_admin | pred.is_admin | pred.is_editor,
"change": pred.is_super_admin | pred.is_admin | pred.is_editor | pred.is_author,
"delete": pred.is_super_admin | pred.is_admin,
}

Expand All @@ -44,8 +66,7 @@ class Meta:
host = models.ForeignKey('Host', on_delete=models.SET_NULL, null=True)

def get_hosts(self):
return self.host

return [self.host]

class Address(models.Model):
name = models.CharField(max_length=200)
Expand Down Expand Up @@ -261,7 +282,7 @@ def user_image_path(user, filename):
return 'images/users/%s%s' % (user.pk, extension)


class User(AbstractBaseUser, PermissionsMixin, RulesModelMixin, metaclass=RulesModelBase):
class User(AbstractBaseUser, PermissionsMixin, RulesModelMixin, AdminThumbnailMixin, metaclass=RulesModelBase):
class Meta:
rules_permissions = {
"add": pred.is_super_admin,
Expand Down Expand Up @@ -520,7 +541,7 @@ def get_teaser(self):
return self.description


class Project(RulesModel):
class Project(AdminThumbnailMixin, RulesModel):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin | pred.is_editor,
Expand Down Expand Up @@ -589,7 +610,7 @@ def search_image(self):
def get_teaser(self):
return self.short_description if self.short_description else self.description

class Event(RulesModelMixin, ScheduleEvent, metaclass=RulesModelBase):
class Event(RulesModelMixin, AdminThumbnailMixin, ScheduleEvent, metaclass=RulesModelBase):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin | pred.is_editor,
Expand Down Expand Up @@ -675,7 +696,7 @@ def get_hosts(self):
return [self.host]


class NewsPost(RulesModel):
class NewsPost(RulesModel, AdminThumbnailMixin):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin | pred.is_editor | pred.is_author,
Expand Down Expand Up @@ -759,7 +780,7 @@ def __str__(self):
return self.title + city


class BlogPost(RulesModel):
class BlogPost(RulesModel, AdminThumbnailMixin):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin | pred.is_editor | pred.is_author,
Expand Down Expand Up @@ -902,7 +923,7 @@ def get_url(self):
return self.url


class Team(RulesModel):
class Team(RulesModel, AdminThumbnailMixin):
class Meta:
rules_permissions = {
"add": pred.is_super_admin | pred.is_admin,
Expand Down
Loading