469 lines
17 KiB
Python
469 lines
17 KiB
Python
# -*- coding: utf-8 -*-
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
|
from __future__ import division, print_function, absolute_import
|
|
|
|
import copy
|
|
from datetime import datetime
|
|
|
|
from django.contrib.auth.models import User, Group
|
|
from django.db import models
|
|
from django.db.models import Max
|
|
from django.conf import settings
|
|
from django.contrib.gis.geoip2 import GeoIP2
|
|
from django.utils.encoding import python_2_unicode_compatible
|
|
|
|
import ox
|
|
from oxdjango.fields import DictField
|
|
from ox.utils import json
|
|
|
|
from itemlist.models import List, Position
|
|
import text
|
|
import edit
|
|
import documentcollection.models
|
|
|
|
from . import managers
|
|
from . import tasks
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class SessionData(models.Model):
|
|
session_key = models.CharField(max_length=40, primary_key=True)
|
|
user = models.OneToOneField(User, null=True, blank=True, related_name='data')
|
|
firstseen = models.DateTimeField(auto_now_add=True, db_index=True)
|
|
lastseen = models.DateTimeField(default=datetime.now, db_index=True)
|
|
username = models.CharField(max_length=255, null=True, db_index=True)
|
|
level = models.IntegerField(default=0, db_index=True)
|
|
|
|
timesseen = models.IntegerField(default=0)
|
|
ip = models.CharField(max_length=255, null=True)
|
|
useragent = models.CharField(max_length=4096, null=True)
|
|
windowsize = models.CharField(max_length=255, null=True)
|
|
screensize = models.CharField(max_length=255, null=True)
|
|
info = DictField(default={})
|
|
|
|
location = models.CharField(max_length=255, null=True)
|
|
location_sort = models.CharField(max_length=255, null=True)
|
|
system = models.CharField(max_length=255, null=True)
|
|
browser = models.CharField(max_length=255, null=True)
|
|
|
|
numberoflists = models.IntegerField(default=0, null=True)
|
|
numberofcollections = models.IntegerField(default=0, null=True)
|
|
|
|
objects = managers.SessionDataManager()
|
|
|
|
groupssort = models.CharField(default=None, blank=True, null=True, max_length=255)
|
|
|
|
def __str__(self):
|
|
return u"%s" % self.session_key
|
|
|
|
def parse_useragent(self):
|
|
if self.useragent:
|
|
ua = ox.parse_useragent(self.useragent)
|
|
self.browser = ua['browser']['string'].lower()
|
|
self.system = ua['system']['string'].lower()
|
|
if not self.browser:
|
|
self.browser = None
|
|
if not self.system:
|
|
self.system = None
|
|
if ua['robot']['name']:
|
|
self.level = -1
|
|
|
|
def parse_data(self):
|
|
self.parse_useragent()
|
|
if self.ip:
|
|
try:
|
|
g = GeoIP2()
|
|
location = g.city(self.ip)
|
|
if location:
|
|
country = ox.get_country_name(location['country_code'])
|
|
if location['city']:
|
|
city = location['city']
|
|
if isinstance(city, bytes):
|
|
city = city.decode('latin-1')
|
|
self.location = u'%s, %s' % (city, country)
|
|
self.location_sort = u'%s, %s' % (country, city)
|
|
else:
|
|
self.location_sort = self.location = country
|
|
else:
|
|
self.location_sort = self.location = None
|
|
except:
|
|
self.location_sort = self.location = None
|
|
pass
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.user:
|
|
self.username = self.user.username
|
|
self.level = self.user.profile.level
|
|
self.firstseen = self.user.date_joined
|
|
if self.user.groups.exists():
|
|
self.groupssort = ''.join([g.name for g in self.user.groups.all()])
|
|
else:
|
|
self.groupssort = None
|
|
self.numberoflists = self.user.lists.count()
|
|
self.numberofcollections = self.user.collections.count()
|
|
else:
|
|
self.groupssort = None
|
|
super(SessionData, self).save(*args, **kwargs)
|
|
|
|
@classmethod
|
|
def get_or_create(cls, request):
|
|
if not request.session.session_key:
|
|
request.session.save()
|
|
request.session.modified = True
|
|
session_key = request.session.session_key
|
|
assert session_key
|
|
if request.user.is_authenticated():
|
|
cls.objects.filter(user=request.user).update(session_key=session_key)
|
|
data, created = cls.objects.get_or_create(session_key=session_key)
|
|
if request.user.is_authenticated():
|
|
data.user = request.user
|
|
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
|
data.ip = request.META['HTTP_X_FORWARDED_FOR'].split(',')[0]
|
|
else:
|
|
data.ip = request.META['REMOTE_ADDR']
|
|
if data.ip.startswith('::ffff:'):
|
|
data.ip = data.ip[len('::ffff:'):]
|
|
data.useragent = request.META.get('HTTP_USER_AGENT', '')[:4096]
|
|
info = json.loads(request.POST.get('data', '{}'))
|
|
if info and isinstance(info, dict):
|
|
data.info = info
|
|
screen = data.info.get('screen', {})
|
|
if screen and 'height' in screen and 'width' in screen:
|
|
data.screensize = u'%s\xd7%s' % (screen['width'], screen['height'])
|
|
window = data.info.get('window', {})
|
|
if window and 'outerHeight' in window and 'outerWidth' in window:
|
|
data.windowsize = u'%s\xd7%s' % (window['outerWidth'], window['outerHeight'])
|
|
if not data.timesseen:
|
|
data.timesseen = 0
|
|
data.timesseen += 1
|
|
data.lastseen = datetime.now()
|
|
data.save()
|
|
tasks.parse_data.delay(data.session_key)
|
|
return data
|
|
|
|
def get_id(self):
|
|
return self.user and ox.toAZ(self.user.id) or self.session_key
|
|
|
|
def json(self, keys=None, user=None):
|
|
ua = ox.parse_useragent(self.useragent or '')
|
|
if ua['robot']['name'] and self.level != -1:
|
|
self.level = -1
|
|
self.save()
|
|
j = {
|
|
'browser': ua['browser']['string'],
|
|
'disabled': False,
|
|
'email': '',
|
|
'firstseen': self.firstseen,
|
|
'ip': self.ip,
|
|
'id': self.get_id(),
|
|
'lastseen': self.lastseen,
|
|
'level': 'robot' if ua['robot']['name'] else 'guest',
|
|
'location': self.location,
|
|
'newsletter': False,
|
|
'notes': '',
|
|
'numberoflists': 0,
|
|
'numberofcollections': 0,
|
|
'screensize': self.screensize,
|
|
'system': ua['system']['string'],
|
|
'timesseen': self.timesseen,
|
|
'username': self.username or '',
|
|
'useragent': self.useragent,
|
|
'windowsize': self.windowsize,
|
|
}
|
|
if self.user:
|
|
p = self.user.profile
|
|
j['disabled'] = not self.user.is_active
|
|
j['email'] = self.user.email
|
|
j['groups'] = [g.name for g in self.user.groups.all()]
|
|
j['level'] = p.get_level()
|
|
j['newsletter'] = p.newsletter
|
|
j['notes'] = p.notes
|
|
j['numberoflists'] = self.numberoflists
|
|
j['numberofcollections'] = self.numberofcollections
|
|
if keys:
|
|
for key in list(j):
|
|
if key not in keys:
|
|
del j[key]
|
|
return j
|
|
|
|
@python_2_unicode_compatible
|
|
class UserProfile(models.Model):
|
|
reset_code = models.CharField(max_length=255, blank=True, null=True, unique=True)
|
|
user = models.OneToOneField(User, related_name='profile')
|
|
|
|
level = models.IntegerField(default=1)
|
|
files_updated = models.DateTimeField(default=datetime.now)
|
|
newsletter = models.BooleanField(default=True)
|
|
ui = DictField(default={})
|
|
preferences = DictField(default={})
|
|
|
|
notes = models.TextField(default='')
|
|
|
|
def __str__(self):
|
|
return self.user.username
|
|
|
|
def get_ui(self):
|
|
return get_ui(self.ui, self.user)
|
|
|
|
def set_level(self, level):
|
|
self.level = settings.CONFIG['userLevels'].index(level)
|
|
if self.level == len(settings.CONFIG['userLevels']) - 1:
|
|
if not self.user.is_superuser:
|
|
self.user.is_superuser = True
|
|
self.user.save()
|
|
elif self.user.is_superuser:
|
|
self.user.is_superuser = False
|
|
self.user.save()
|
|
|
|
def get_level(self):
|
|
#django superuser should always be admin
|
|
if self.user.is_superuser:
|
|
if self.level != len(settings.CONFIG['userLevels']) - 1:
|
|
self.level = len(settings.CONFIG['userLevels']) - 1
|
|
self.save()
|
|
return settings.CONFIG['userLevels'][self.level]
|
|
|
|
def capability(self, capability):
|
|
return settings.CONFIG['capabilities'][capability].get(self.get_level()) == True
|
|
|
|
def user_post_save(sender, instance, **kwargs):
|
|
profile, new = UserProfile.objects.get_or_create(user=instance)
|
|
if new and instance.is_superuser:
|
|
profile.level = len(settings.CONFIG['userLevels']) - 1
|
|
profile.newsletter = True
|
|
profile.save()
|
|
SessionData.objects.filter(user=instance).update(level=profile.level,
|
|
username=instance.username)
|
|
models.signals.post_save.connect(user_post_save, sender=User)
|
|
|
|
def profile_post_save(sender, instance, **kwargs):
|
|
SessionData.objects.filter(user=instance.user).update(level=instance.level,
|
|
username=instance.user.username)
|
|
models.signals.post_save.connect(profile_post_save, sender=UserProfile)
|
|
|
|
def get_ui(user_ui, user=None):
|
|
ui = {}
|
|
config = copy.deepcopy(settings.CONFIG)
|
|
ui.update(config['user']['ui'])
|
|
|
|
def update_ui(ui, new):
|
|
'''
|
|
only update set keys in dicts
|
|
'''
|
|
for key in new:
|
|
if isinstance(new[key], dict) and key in ui:
|
|
ui[key] = update_ui(ui[key], new[key])
|
|
elif isinstance(ui, dict):
|
|
ui[key] = new[key]
|
|
return ui
|
|
ui = update_ui(ui, user_ui)
|
|
if 'lists' not in ui:
|
|
ui['lists'] = {}
|
|
|
|
if 'collections' not in ui:
|
|
ui['collections'] = {}
|
|
|
|
def add(lists, section):
|
|
ids = []
|
|
for l in lists:
|
|
qs = Position.objects.filter(section=section)
|
|
if section == 'featured':
|
|
try:
|
|
pos = Position.objects.get(list=l, section=section)
|
|
created = False
|
|
except Position.DoesNotExist:
|
|
pos = Position(list=l, section=section, user=l.user)
|
|
pos.save()
|
|
created = True
|
|
else:
|
|
pos, created = Position.objects.get_or_create(list=l, user=user, section=section)
|
|
qs = qs.filter(user=user)
|
|
if created:
|
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
|
pos.save()
|
|
id = l.get_id()
|
|
'''
|
|
if id not in ui['lists']:
|
|
ui['lists'][id] = {}
|
|
ui['lists'][id].update(ui['lists'][''])
|
|
'''
|
|
ids.append(id)
|
|
return ids
|
|
|
|
def add_collections(collections, section):
|
|
Position = documentcollection.models.Position
|
|
ids = []
|
|
for l in collections:
|
|
qs = Position.objects.filter(section=section)
|
|
if section == 'featured':
|
|
try:
|
|
pos = Position.objects.get(collection=l, section=section)
|
|
created = False
|
|
except Position.DoesNotExist:
|
|
pos = Position(collection=l, section=section, user=l.user)
|
|
pos.save()
|
|
created = True
|
|
else:
|
|
pos, created = Position.objects.get_or_create(collection=l, user=user, section=section)
|
|
qs = qs.filter(user=user)
|
|
if created:
|
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
|
pos.save()
|
|
id = l.get_id()
|
|
ids.append(id)
|
|
return ids
|
|
|
|
def add_texts(texts, section):
|
|
P = text.models.Position
|
|
ids = []
|
|
for t in texts:
|
|
qs = P.objects.filter(section=section)
|
|
if section == 'featured':
|
|
try:
|
|
pos = P.objects.get(text=t, section=section)
|
|
created = False
|
|
except P.DoesNotExist:
|
|
pos = P(text=t, section=section, user=t.user)
|
|
pos.save()
|
|
created = True
|
|
else:
|
|
pos, created = P.objects.get_or_create(text=t, user=user, section=section)
|
|
qs = qs.filter(user=user)
|
|
if created:
|
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
|
pos.save()
|
|
ids.append(t.get_id())
|
|
return ids
|
|
|
|
def add_edits(edits, section):
|
|
P = edit.models.Position
|
|
ids = []
|
|
for t in edits:
|
|
qs = P.objects.filter(section=section)
|
|
if section == 'featured':
|
|
try:
|
|
pos = P.objects.get(edit=t, section=section)
|
|
created = False
|
|
except P.DoesNotExist:
|
|
pos = P(edit=t, section=section, user=t.user)
|
|
pos.save()
|
|
created = True
|
|
else:
|
|
pos, created = P.objects.get_or_create(edit=t, user=user, section=section)
|
|
qs = qs.filter(user=user)
|
|
if created:
|
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
|
pos.save()
|
|
ids.append(t.get_id())
|
|
return ids
|
|
|
|
# lists (items)
|
|
ids = ['']
|
|
if user:
|
|
ids += add(user.lists.exclude(status="featured"), 'personal')
|
|
ids += add(user.subscribed_lists.filter(status='public'), 'public')
|
|
ids += add(List.objects.filter(status='featured'), 'featured')
|
|
for i in list(ui['lists']):
|
|
if i not in ids:
|
|
del ui['lists'][i]
|
|
|
|
# collections (documents)
|
|
ids = ['']
|
|
if user:
|
|
ids += add_collections(user.collections.exclude(status="featured"), 'personal')
|
|
ids += add_collections(user.subscribed_collections.filter(status='public'), 'public')
|
|
ids += add_collections(documentcollection.models.Collection.objects.filter(status='featured'), 'featured')
|
|
for i in list(ui['collections']):
|
|
if i not in ids:
|
|
del ui['collections'][i]
|
|
|
|
# texts (remove)
|
|
tids = ['']
|
|
if user:
|
|
tids += add_texts(user.texts.exclude(status="featured"), 'personal')
|
|
tids += add_texts(user.subscribed_texts.filter(status='public'), 'public')
|
|
tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured')
|
|
|
|
# edits
|
|
tids = ['']
|
|
if user:
|
|
tids += add_edits(user.edits.exclude(status="featured"), 'personal')
|
|
tids += add_edits(user.subscribed_edits.filter(status='public'), 'public')
|
|
tids += add_edits(edit.models.Edit.objects.filter(status='featured'), 'featured')
|
|
|
|
if 'filters' in ui:
|
|
filterids = [f['id'] for f in settings.CONFIG['itemKeys'] if f.get('filter')]
|
|
ui['filters'] = [f for f in ui['filters'] if f['id'] in filterids]
|
|
used = [f['id'] for f in ui['filters']]
|
|
unused = [f for f in settings.CONFIG['user']['ui']['filters'] if f['id'] not in used]
|
|
while len(ui['filters']) < len(settings.CONFIG['user']['ui']['filters']):
|
|
ui['filters'].append(unused.pop())
|
|
|
|
return ui
|
|
|
|
def init_user(user, request=None):
|
|
if request:
|
|
SessionData.get_or_create(request)
|
|
if user.is_anonymous():
|
|
result = settings.CONFIG['user'].copy()
|
|
result['ui'] = get_ui(json.loads(request.session.get('ui', '{}')))
|
|
else:
|
|
profile = user.profile
|
|
result = {}
|
|
for key in ('username', ):
|
|
result[key] = getattr(user, key)
|
|
result['level'] = profile.get_level()
|
|
result['groups'] = [g.name for g in user.groups.all()]
|
|
result['email'] = user.email
|
|
result['newsletter'] = profile.newsletter
|
|
result['ui'] = profile.get_ui()
|
|
result['volumes'] = [v.json() for v in user.volumes.all()]
|
|
result['script'] = profile.preferences.get('script', '')
|
|
return result
|
|
|
|
def user_json(user, keys=None):
|
|
p = user.profile
|
|
j = {
|
|
'disabled': not user.is_active,
|
|
'email': user.email,
|
|
'firstseen': user.date_joined,
|
|
'id': ox.toAZ(user.id),
|
|
'lastseen': user.last_login,
|
|
'level': p.get_level(),
|
|
'newsletter': p.newsletter,
|
|
'notes': p.notes,
|
|
'numberoflists': user.lists.count(),
|
|
'script': p.preferences.get('script', ''),
|
|
'username': user.username,
|
|
}
|
|
if keys:
|
|
for key in list(j):
|
|
if key not in keys:
|
|
del j[key]
|
|
return j
|
|
|
|
def has_capability(user, capability):
|
|
if user.is_anonymous():
|
|
level = 'guest'
|
|
else:
|
|
level = user.profile.get_level()
|
|
return level in settings.CONFIG['capabilities'][capability] \
|
|
and settings.CONFIG['capabilities'][capability][level]
|
|
|
|
def merge_users(old, new):
|
|
old.annotations.all().update(user=new)
|
|
old.edits.all().update(user=new)
|
|
old.entities.all().update(user=new)
|
|
old.events.all().update(user=new)
|
|
old.files.all().update(user=new)
|
|
old.items.all().update(user=new)
|
|
old.lits.all().update(user=new)
|
|
old.places.all().update(user=new)
|
|
old.subscribed_edits.all().update(user=new)
|
|
old.subscribed_lists.all().update(user=new)
|
|
old.subscribed_texts.all().update(user=new)
|
|
old.texts.all().update(user=new)
|
|
old.volumes.all().update(user=new)
|
|
old.log_set.all().update(user=new)
|
|
old.changelog.all().update(user=new)
|
|
old.logentry_set.all().update(user=new)
|