port to new django, AI automated
2
.gitignore
vendored
@@ -4,5 +4,3 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/._bootstrapTemplates
|
/._bootstrapTemplates
|
||||||
/env
|
/env
|
||||||
/venv
|
|
||||||
/bower_components
|
|
||||||
24
Dockerfile
@@ -1,24 +0,0 @@
|
|||||||
# docker build -t "blechreiz_website" .
|
|
||||||
FROM ubuntu:20.04
|
|
||||||
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y python3 python3-venv python3-wheel \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
ADD . /blechreiz
|
|
||||||
ENV BLECHREIZ_DATA /data
|
|
||||||
ENV PYTHONUNBUFFERED 1
|
|
||||||
ENV PYTHONPATH /blechreiz
|
|
||||||
|
|
||||||
RUN cd /blechreiz \
|
|
||||||
&& python3 -m venv env \
|
|
||||||
&& . /blechreiz/env/bin/activate \
|
|
||||||
&& rm -r /blechreiz/blechreiz/data \
|
|
||||||
&& /blechreiz/env/bin/pip install -r requirements.txt
|
|
||||||
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
CMD /blechreiz/env/bin/python3 /blechreiz/manage.py collectstatic --noinput \
|
|
||||||
&& /blechreiz/env/bin/gunicorn -b 0.0.0.0:8000 blechreiz.wsgi
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"_module": "oauth2client.client", "token_expiry": "2018-12-18T12:55:20Z", "access_token": "ya29.Gl12BuJlFBtRZSBtMkxubhfG8rYDsLUgc1yKMpO9_aE3tmi4sLFGLq_Djq5R9zr1919oZAobX0oaD6bTj3s4i9kqWfw0gpOGKJKRHM1Y9gHc0JsCe2kChNszu_RCAaE", "token_uri": "https://accounts.google.com/o/oauth2/token", "invalid": false, "token_response": {"access_token": "ya29.Gl12BuJlFBtRZSBtMkxubhfG8rYDsLUgc1yKMpO9_aE3tmi4sLFGLq_Djq5R9zr1919oZAobX0oaD6bTj3s4i9kqWfw0gpOGKJKRHM1Y9gHc0JsCe2kChNszu_RCAaE", "scope": "https://www.googleapis.com/auth/calendar", "expires_in": 3600, "token_type": "Bearer"}, "client_id": "34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com", "id_token": null, "client_secret": "y4t9XBrJdCODPTO5UvtONWWn", "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", "_class": "OAuth2Credentials", "refresh_token": "1/txixroRJyyVmuENPpaXyB_bGeXa1XV-pClAxqKHk_5-JW1qGFE0Gl-WlgCu1Eizq", "user_agent": null}
|
|
||||||
@@ -1,37 +1,41 @@
|
|||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.conf import settings
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
|
|
||||||
class EnforceLoginMiddleware:
|
class EnforceLoginMiddleware:
|
||||||
"""
|
"""
|
||||||
Middlware class which requires the user to be authenticated for all urls except
|
Middleware class which requires the user to be authenticated for all urls except
|
||||||
those defined in PUBLIC_URLS in settings.py. PUBLIC_URLS should be a tuple of regular
|
those defined in PUBLIC_URLS in settings.py. PUBLIC_URLS should be a tuple of regular
|
||||||
expresssions for the urls you want anonymous users to have access to. If PUBLIC_URLS
|
expressions for the urls you want anonymous users to have access to. If PUBLIC_URLS
|
||||||
is not defined, it falls back to LOGIN_URL or failing that '/accounts/login/'.
|
is not defined, it falls back to LOGIN_URL or failing that '/accounts/login/'.
|
||||||
Requests for urls not matching PUBLIC_URLS get redirected to LOGIN_URL with next set
|
Requests for urls not matching PUBLIC_URLS get redirected to LOGIN_URL with next set
|
||||||
to original path of the unauthenticted request.
|
to original path of the unauthenticated request.
|
||||||
Any urls statically served by django are excluded from this check. To enforce the same
|
Any urls statically served by django are excluded from this check. To enforce the same
|
||||||
validation on these set SERVE_STATIC_TO_PUBLIC to False.
|
validation on these set SERVE_STATIC_TO_PUBLIC to False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.login_url = getattr(settings, 'LOGIN_URL', '/accounts/login/')
|
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
self.login_url = getattr(settings, "LOGIN_URL", "/accounts/login/")
|
||||||
|
|
||||||
if hasattr(settings, 'PUBLIC_URLS'):
|
if hasattr(settings, "PUBLIC_URLS"):
|
||||||
public_urls = [re.compile(url) for url in settings.PUBLIC_URLS]
|
public_urls = [re.compile(url) for url in settings.PUBLIC_URLS]
|
||||||
else:
|
else:
|
||||||
public_urls = [(re.compile("^%s/?$" % (self.login_url[1:])))]
|
public_urls = [re.compile("^%s/?$" % (self.login_url[1:]))]
|
||||||
if getattr(settings, 'SERVE_STATIC_TO_PUBLIC', True):
|
|
||||||
root_urlconf = __import__(settings.ROOT_URLCONF)
|
|
||||||
public_urls.extend([re.compile(url.regex)
|
|
||||||
for url in root_urlconf.urls.urlpatterns
|
|
||||||
if url.__dict__.get('_callback_str') == 'django.views.static.serve'
|
|
||||||
])
|
|
||||||
self.public_urls = tuple(public_urls)
|
self.public_urls = tuple(public_urls)
|
||||||
|
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
|
# Check if user needs to be redirected to login
|
||||||
|
redirect_response = self.check_login(request)
|
||||||
|
if redirect_response:
|
||||||
|
return redirect_response
|
||||||
|
|
||||||
|
return self.get_response(request)
|
||||||
|
|
||||||
|
def check_login(self, request):
|
||||||
"""
|
"""
|
||||||
Redirect anonymous users to login_url from non public urls
|
Redirect anonymous users to login_url from non public urls
|
||||||
"""
|
"""
|
||||||
@@ -40,52 +44,63 @@ class EnforceLoginMiddleware:
|
|||||||
for url in self.public_urls:
|
for url in self.public_urls:
|
||||||
if url.match(request.path[1:]):
|
if url.match(request.path[1:]):
|
||||||
return None
|
return None
|
||||||
return HttpResponseRedirect("%s?next=%s" % (self.login_url, request.path))
|
return HttpResponseRedirect(
|
||||||
|
"%s?next=%s" % (self.login_url, request.path)
|
||||||
|
)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return HttpResponseRedirect("%s?next=%s" % (self.login_url, request.path))
|
return HttpResponseRedirect("%s?next=%s" % (self.login_url, request.path))
|
||||||
|
return None
|
||||||
return self.get_response(request)
|
|
||||||
|
|
||||||
|
|
||||||
class DetectDevice:
|
class DetectDevice:
|
||||||
|
"""
|
||||||
|
Middleware to detect the device type from user agent string.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
device = self.mobile(request)
|
request.device = self.detect_device(request)
|
||||||
|
return self.get_response(request)
|
||||||
|
|
||||||
request.device = device
|
def detect_device(self, request):
|
||||||
response = self.get_response(request)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def mobile(self, request):
|
|
||||||
device = {}
|
device = {}
|
||||||
|
|
||||||
ua = request.META.get('HTTP_USER_AGENT', '').lower()
|
ua = request.META.get("HTTP_USER_AGENT", "").lower()
|
||||||
|
|
||||||
if ua.find("iphone") > 0:
|
if "iphone" in ua:
|
||||||
device['iphone'] = "iphone" + re.search("iphone os (\d)", ua).groups(0)[0]
|
match = re.search(r"iphone os (\d)", ua)
|
||||||
|
if match:
|
||||||
|
device["iphone"] = "iphone" + match.group(1)
|
||||||
|
else:
|
||||||
|
device["iphone"] = "iphone"
|
||||||
|
|
||||||
if ua.find("ipad") > 0:
|
if "ipad" in ua:
|
||||||
device['ipad'] = "ipad"
|
device["ipad"] = "ipad"
|
||||||
|
|
||||||
if ua.find("android") > 0:
|
if "android" in ua:
|
||||||
device['android'] = "android" + re.search("android (\d\.\d)", ua).groups(0)[0].translate(None, '.')
|
match = re.search(r"android (\d\.\d)", ua)
|
||||||
|
if match:
|
||||||
|
version = match.group(1).replace(".", "")
|
||||||
|
device["android"] = "android" + version
|
||||||
|
else:
|
||||||
|
device["android"] = "android"
|
||||||
|
|
||||||
if ua.find("blackberry") > 0:
|
if "blackberry" in ua:
|
||||||
device['blackberry'] = "blackberry"
|
device["blackberry"] = "blackberry"
|
||||||
|
|
||||||
if ua.find("windows phone os 7") > 0:
|
if "windows phone os 7" in ua:
|
||||||
device['winphone7'] = "winphone7"
|
device["winphone7"] = "winphone7"
|
||||||
|
|
||||||
if ua.find("iemobile") > 0:
|
if "iemobile" in ua:
|
||||||
device['winmo'] = "winmo"
|
device["winmo"] = "winmo"
|
||||||
|
|
||||||
if not device: # either desktop, or something we don't care about.
|
if not device:
|
||||||
device['baseline'] = "baseline"
|
# either desktop, or something we don't care about.
|
||||||
|
device["baseline"] = "baseline"
|
||||||
|
|
||||||
# spits out device names for CSS targeting, to be applied to <html> or <body>.
|
# spits out device names for CSS targeting, to be applied to <html> or <body>.
|
||||||
device['classes'] = " ".join(v for (k, v) in device.items())
|
device["classes"] = " ".join(v for (k, v) in device.items())
|
||||||
|
|
||||||
return device
|
return device
|
||||||
|
|||||||
@@ -1,233 +1,205 @@
|
|||||||
# Setting the path
|
# Setting the path
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
gettext = lambda s: s
|
BASE_DIR = Path(__file__).resolve().parent
|
||||||
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
|
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
DATA_PATH = os.environ.get('BLECHREIZ_DATA', default=os.path.join(PROJECT_PATH, "data"))
|
|
||||||
|
|
||||||
|
|
||||||
# Django settings for blechreiz project.
|
# Django settings for blechreiz project.
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
|
||||||
|
|
||||||
ADMINS = (
|
ADMINS = [
|
||||||
('Martin Bauer', 'bauer_martin@gmx.de'),
|
("Martin Bauer", "bauer_martin@gmx.de"),
|
||||||
)
|
]
|
||||||
|
|
||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': os.path.join(DATA_PATH, 'database.sqlite'),
|
"NAME": os.path.join(PROJECT_PATH, "database.sqlite"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
|
|
||||||
EMAIL_HOST = 'smtp.blechreiz.com'
|
EMAIL_HOST = "smtp.blechreiz.com"
|
||||||
EMAIL_HOST_USER = 'm02b721a'
|
EMAIL_HOST_USER = "m02b721a"
|
||||||
EMAIL_HOST_PASSWORD = '9Hp4WG5bZ2WVPX5z'
|
EMAIL_HOST_PASSWORD = "9Hp4WG5bZ2WVPX5z"
|
||||||
EMAIL_USE_TLS = False
|
EMAIL_USE_TLS = False
|
||||||
|
|
||||||
|
|
||||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||||
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
|
ALLOWED_HOSTS = ["localhost", "127.0.0.1", ".blechreiz.com", ".bauer.technology"]
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
# Local time zone for this installation.
|
||||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
TIME_ZONE = "Europe/Berlin"
|
||||||
# although not all choices may be available on all operating systems.
|
|
||||||
# In a Windows environment this must be set to your system time zone.
|
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
# Language code for this installation.
|
||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
LANGUAGE_CODE = "de"
|
||||||
LANGUAGE_CODE = 'de'
|
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
# If you set this to False, Django will make some optimizations so as not
|
# Internationalization
|
||||||
# to load the internationalization machinery.
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
# If you set this to False, Django will not format dates, numbers and
|
|
||||||
# calendars according to the current locale.
|
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||||
# Example: "/var/www/example.com/media/"
|
MEDIA_ROOT = PROJECT_PATH + "/media/"
|
||||||
MEDIA_ROOT = PROJECT_PATH + "/media"
|
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
# URL that handles the media served from MEDIA_ROOT.
|
||||||
# trailing slash.
|
MEDIA_URL = "/media/"
|
||||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
|
||||||
MEDIA_URL = '/media/'
|
|
||||||
|
|
||||||
# Absolute path to the directory static files should be collected to.
|
# Absolute path to the directory static files should be collected to.
|
||||||
# Don't put anything in this directory yourself; store your static files
|
STATIC_ROOT = PROJECT_PATH + "/static_collection"
|
||||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
|
||||||
# Example: "/var/www/example.com/static/"
|
|
||||||
STATIC_ROOT = PROJECT_PATH + '/static_collection'
|
|
||||||
|
|
||||||
# URL prefix for static files.
|
# URL prefix for static files.
|
||||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
STATIC_URL = "/static/"
|
||||||
STATIC_URL = '/static/'
|
|
||||||
|
|
||||||
LOGIN_URL = "/musicians/login"
|
LOGIN_URL = "/musicians/login"
|
||||||
PUBLIC_URLS = ("^musicians/login/?$", "^musicians/login/usernames/?$", "^eventplanner_gcal/gcalApiCallback*")
|
PUBLIC_URLS = (
|
||||||
|
"^musicians/login/?$",
|
||||||
|
"^musicians/login/usernames/?$",
|
||||||
|
"^eventplanner_gcal/gcalApiCallback*",
|
||||||
|
)
|
||||||
|
|
||||||
# Additional locations of static files
|
# Additional locations of static files
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = [
|
||||||
#PROJECT_PATH + '/static',
|
PROJECT_PATH + "/static",
|
||||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
]
|
||||||
# Always use forward slashes, even on Windows.
|
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
|
||||||
)
|
|
||||||
|
|
||||||
# List of finder classes that know how to find static files in
|
# List of finder classes that know how to find static files in various locations.
|
||||||
# various locations.
|
STATICFILES_FINDERS = [
|
||||||
STATICFILES_FINDERS = (
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
]
|
||||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
# Make this unique, and don't share it with anybody.
|
||||||
SECRET_KEY = 'x$8&9s6t%eeg=wyhar87934wh_s$dbpm(73g4ho&n)9_wogj6p'
|
SECRET_KEY = "x$8&9s6t%eeg=wyhar87934wh_s$dbpm(73g4ho&n)9_wogj6p"
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
"django.middleware.common.CommonMiddleware",
|
||||||
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"blechreiz.middleware.EnforceLoginMiddleware",
|
||||||
|
"blechreiz.middleware.DetectDevice",
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = "blechreiz.urls"
|
||||||
|
|
||||||
|
# Python dotted path to the WSGI application used by Django's runserver.
|
||||||
|
WSGI_APPLICATION = "blechreiz.wsgi.application"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [
|
"DIRS": [
|
||||||
PROJECT_PATH + '/templates',
|
PROJECT_PATH + "/templates",
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
# Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this
|
"django.template.context_processors.debug",
|
||||||
# list if you haven't customized them:
|
"django.template.context_processors.request",
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.template.context_processors.i18n',
|
"django.contrib.messages.context_processors.messages",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.i18n",
|
||||||
'django.template.context_processors.media',
|
"django.template.context_processors.media",
|
||||||
'django.template.context_processors.static',
|
"django.template.context_processors.static",
|
||||||
'sekizai.context_processors.sekizai',
|
"sekizai.context_processors.sekizai",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = (
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
#'blechreiz.middleware.EnforceLoginMiddleware',
|
|
||||||
'blechreiz.middleware.DetectDevice',
|
|
||||||
# Uncomment the next line for simple clickjacking protection:
|
|
||||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'blechreiz.urls'
|
INSTALLED_APPS = [
|
||||||
|
"django.contrib.admin",
|
||||||
# Python dotted path to the WSGI application used by Django's runserver.
|
"django.contrib.auth",
|
||||||
WSGI_APPLICATION = 'blechreiz.wsgi.application'
|
"django.contrib.contenttypes",
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.sites",
|
||||||
INSTALLED_APPS = (
|
"django.contrib.messages",
|
||||||
'django.contrib.auth',
|
"django.contrib.staticfiles",
|
||||||
'django.contrib.contenttypes',
|
# Third-party apps
|
||||||
'django.contrib.sessions',
|
"crispy_forms",
|
||||||
'django.contrib.sites',
|
"crispy_bootstrap5",
|
||||||
'django.contrib.messages',
|
"sekizai",
|
||||||
'django.contrib.staticfiles',
|
"rest_framework",
|
||||||
'django.contrib.admin',
|
# Own apps
|
||||||
'django.contrib.admindocs',
|
"bootstrapTheme",
|
||||||
'crispy_forms', # better looking forms ( bootstrap )
|
"website",
|
||||||
'sekizai', # for the addtoblock directive in templates
|
"musicians",
|
||||||
'rest_framework', # for event management api
|
"eventplanner",
|
||||||
|
"eventplanner_gcal",
|
||||||
# Own Things
|
"simpleforum",
|
||||||
'bootstrapTheme', # Theme
|
"location_field",
|
||||||
'website', # Blechreiz Website in general
|
"scoremanager",
|
||||||
'musicians', # User Management
|
# 'imagestore', # Disabled
|
||||||
'eventplanner', # Event Management
|
]
|
||||||
'eventplanner_gcal', # Event Management Sync with Google Calendar
|
|
||||||
'simpleforum', # Messages ( Forum )
|
|
||||||
'location_field', # custom location field used in Event Management
|
|
||||||
'scoremanager', # manager of scores, repertoire etc.
|
|
||||||
# 'imagestore',
|
|
||||||
# 'sorl.thumbnail',
|
|
||||||
# 'tagging'
|
|
||||||
)
|
|
||||||
|
|
||||||
IMAGESTORE_TEMPLATE = "website/base.html"
|
IMAGESTORE_TEMPLATE = "website/base.html"
|
||||||
|
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
|
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
|
||||||
'PAGINATE_BY': 10
|
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
||||||
|
"PAGE_SIZE": 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GCAL_COUPLING = {
|
GCAL_COUPLING = {
|
||||||
'eventPrefix': 'Blechreiz: ',
|
"eventPrefix": "Blechreiz: ",
|
||||||
'developerKey': 'blechreiz-homepage',
|
"developerKey": "blechreiz-homepage",
|
||||||
'clientId': '34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com',
|
"clientId": "34462582242-4kpdvvbi27ajt4u22uitqurpve9o8ipj.apps.googleusercontent.com",
|
||||||
'client_secret': 'y4t9XBrJdCODPTO5UvtONWWn',
|
"client_secret": "y4t9XBrJdCODPTO5UvtONWWn",
|
||||||
'credentials_file': DATA_PATH + '/calendarCredentials.dat',
|
"credentials_file": PROJECT_PATH + "/calendarCredentials.dat",
|
||||||
'push_url': "https://blechreiz.bauer.technology/eventplanner_gcal/gcalApiCallback",
|
"push_url": "https://blechreiz.bauer.technology/eventplanner_gcal/gcalApiCallback",
|
||||||
}
|
}
|
||||||
GOOGLE_MAPS_API_KEY = 'AIzaSyCf9Lm5ckjmVd08scTOd7fB1dC_UCoumKg'
|
|
||||||
GCAL_SYNC_ENABLED = True
|
|
||||||
|
|
||||||
|
|
||||||
CRISPY_TEMPLATE_PACK = 'bootstrap'
|
# Crispy Forms configuration
|
||||||
|
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||||
|
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||||
|
|
||||||
# A sample logging configuration. The only tangible logging
|
|
||||||
# performed by this configuration is to send an email to
|
|
||||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
|
||||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
|
||||||
# more details on how to customize your logging configuration.
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
"version": 1,
|
||||||
'disable_existing_loggers': False,
|
"disable_existing_loggers": False,
|
||||||
'formatters': {
|
"handlers": {
|
||||||
'verbose': {
|
"file": {
|
||||||
'format': '{levelname} {asctime} {module} {message}',
|
"level": "DEBUG",
|
||||||
'style': '{',
|
"class": "logging.FileHandler",
|
||||||
|
"filename": PROJECT_PATH + "/eventplanner.log",
|
||||||
},
|
},
|
||||||
'simple': {
|
"console": {
|
||||||
'format': '{levelname} {asctime} {message}',
|
"level": "DEBUG",
|
||||||
'style': '{',
|
"class": "logging.StreamHandler",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'handlers': {
|
"loggers": {
|
||||||
'file': {
|
"eventplanner_gcal": {
|
||||||
'level': 'DEBUG',
|
"handlers": ["file", "console"],
|
||||||
'class': 'logging.FileHandler',
|
"level": "DEBUG",
|
||||||
'filename': DATA_PATH + '/eventplanner.log',
|
"propagate": True,
|
||||||
|
},
|
||||||
|
"eventplanner": {
|
||||||
|
"handlers": ["file", "console"],
|
||||||
|
"level": "DEBUG",
|
||||||
|
"propagate": True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'loggers': {
|
|
||||||
'eventplanner_gcal': {
|
|
||||||
'handlers': ['file'],
|
|
||||||
'level': 'DEBUG',
|
|
||||||
'propagate': True,
|
|
||||||
},
|
|
||||||
'eventplanner': {
|
|
||||||
'handler': ['file'],
|
|
||||||
'level': 'DEBUG',
|
|
||||||
'propagate': True,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,21 @@
|
|||||||
from django.conf.urls import include, url
|
from django.conf import settings
|
||||||
|
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
import simpleforum.views
|
|
||||||
|
|
||||||
import eventplanner.urls
|
|
||||||
import musicians.urls
|
|
||||||
import website.urls
|
|
||||||
import scoremanager.urls
|
|
||||||
import eventplanner_gcal.urls
|
|
||||||
|
|
||||||
from . import settings
|
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
admin.autodiscover()
|
from simpleforum import views as simpleforum_views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^', include(website.urls)),
|
path("", include("website.urls")),
|
||||||
url(r'^events/', include(eventplanner.urls.urlpatterns)),
|
path("events/", include("eventplanner.urls")),
|
||||||
url(r'^musicians/', include(musicians.urls.urlpatterns)),
|
path("musicians/", include("musicians.urls")),
|
||||||
url(r'^scores/', include(scoremanager.urls.urlpatterns)),
|
path("scores/", include("scoremanager.urls")),
|
||||||
url(r'^messages/$', simpleforum.views.message_view),
|
path("messages/", simpleforum_views.message_view),
|
||||||
url(r'^admin/', admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
url(r'^location_field/', include('location_field.urls')),
|
path("location_field/", include("location_field.urls")),
|
||||||
url(r'^eventplanner_gcal/', include(eventplanner_gcal.urls)),
|
path("eventplanner_gcal/", include("eventplanner_gcal.urls")),
|
||||||
# url(r'^gallery/', include(imagestore.urls, namespace='imagestore') ),
|
# url(r'^gallery/', include('imagestore.urls', namespace='imagestore')),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
]
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -1,32 +1,16 @@
|
|||||||
"""
|
"""
|
||||||
WSGI config for blechreiz project.
|
WSGI config for blechreiz project.
|
||||||
|
|
||||||
This module contains the WSGI application used by Django's development server
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
and any production WSGI deployments. It should expose a module-level variable
|
|
||||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
|
||||||
this application via the ``WSGI_APPLICATION`` setting.
|
|
||||||
|
|
||||||
Usually you will have the standard Django WSGI application here, but it also
|
|
||||||
might make sense to replace the whole Django WSGI application with a custom one
|
|
||||||
that later delegates to the Django one. For example, you could introduce WSGI
|
|
||||||
middleware here, or combine a Django application with an application of another
|
|
||||||
framework.
|
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
from django.core.wsgi import get_wsgi_application
|
||||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
|
||||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
|
||||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "blechreiz.settings"
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blechreiz.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blechreiz.settings")
|
||||||
|
|
||||||
# This application object is used by any WSGI server configured to use this
|
|
||||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
|
||||||
# setting points here.
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
# Apply WSGI middleware here.
|
|
||||||
# from helloworld.wsgi import HelloWorldApplication
|
|
||||||
# application = HelloWorldApplication(application)
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
@@ -1,37 +1,17 @@
|
|||||||
/* German initialisation for the jQuery UI date picker plugin. */
|
/**
|
||||||
/* Written by Milian Wolff (mail@milianw.de). */
|
* German translation for bootstrap-datepicker
|
||||||
( function( factory ) {
|
* Sam Zurcher <sam@orelias.ch>
|
||||||
if ( typeof define === "function" && define.amd ) {
|
*/
|
||||||
|
;(function($){
|
||||||
// AMD. Register as an anonymous module.
|
$.fn.datepicker.dates['de'] = {
|
||||||
define( [ "../widgets/datepicker" ], factory );
|
days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"],
|
||||||
} else {
|
daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"],
|
||||||
|
daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"],
|
||||||
// Browser globals
|
months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
|
||||||
factory( jQuery.datepicker );
|
monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
|
||||||
}
|
today: "Heute",
|
||||||
}( function( datepicker ) {
|
clear: "Löschen",
|
||||||
|
weekStart: 1,
|
||||||
datepicker.regional.de = {
|
format: "dd.mm.yyyy"
|
||||||
closeText: "Schließen",
|
};
|
||||||
prevText: "<Zurück",
|
}(jQuery));
|
||||||
nextText: "Vor>",
|
|
||||||
currentText: "Heute",
|
|
||||||
monthNames: [ "Januar","Februar","März","April","Mai","Juni",
|
|
||||||
"Juli","August","September","Oktober","November","Dezember" ],
|
|
||||||
monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun",
|
|
||||||
"Jul","Aug","Sep","Okt","Nov","Dez" ],
|
|
||||||
dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ],
|
|
||||||
dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
|
|
||||||
dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
|
|
||||||
weekHeader: "KW",
|
|
||||||
dateFormat: "dd.mm.yy",
|
|
||||||
firstDay: 1,
|
|
||||||
isRTL: false,
|
|
||||||
showMonthAfterYear: false,
|
|
||||||
yearSuffix: "" };
|
|
||||||
datepicker.setDefaults( datepicker.regional.de );
|
|
||||||
|
|
||||||
return datepicker.regional.de;
|
|
||||||
|
|
||||||
} ) );
|
|
||||||
13
bootstrapTheme/static/js/jquery-ui-1.12.1.min.js
vendored
@@ -1,62 +1,72 @@
|
|||||||
{% load sekizai_tags staticfiles %}
|
{% load sekizai_tags static %} {% addtoblock "css" strip %}
|
||||||
|
<link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap.min.css" />{% endaddtoblock %} {% addtoblock "css" strip %}
|
||||||
|
<link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap-responsive.min.css" />
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}css/bootstrap.min.css" > {% endaddtoblock %}
|
{% endaddtoblock %} {% addtoblock "css" strip %}
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}css/bootstrap-responsive.min.css" > {% endaddtoblock %}
|
<link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap-overrides.css" />{% endaddtoblock %} {% addtoblock "css" strip %}
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}css/bootstrap-overrides.css"> {% endaddtoblock %}
|
<link rel="stylesheet" href="{{STATIC_URL}}/css/theme.css" type="text/css" />{% endaddtoblock %} {% addtoblock "css" strip %}
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}css/theme.css" type="text/css"> {% endaddtoblock %}
|
<link
|
||||||
{% addtoblock "css" strip %} <link rel="stylesheet" href="{{STATIC_URL}}css/index.css" type="text/css" media="screen" /> {% endaddtoblock %}
|
rel="stylesheet"
|
||||||
{% addtoblock "css" strip %} <link href='https://fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic' rel='stylesheet' type='text/css'>{% endaddtoblock %}
|
href="{{STATIC_URL}}/css/index.css"
|
||||||
|
type="text/css"
|
||||||
{% addtoblock "css" %}
|
media="screen"
|
||||||
<!--[if lt IE 9]>
|
/>
|
||||||
|
{% endaddtoblock %} {% addtoblock "css" strip %}
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>{% endaddtoblock %} {% addtoblock "css" %}
|
||||||
|
<!--[if lt IE 9]>
|
||||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
|
<script src="{{STATIC_URL}}/js/jquery-2.0.3.min.js"></script>
|
||||||
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
|
<script src="{{STATIC_URL}}/js/bootstrap.min.js"></script>
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/jquery-2.0.3.min.js"></script> {% endaddtoblock %}
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/bootstrap.min.js"></script> {% endaddtoblock %}
|
<script src="{{STATIC_URL}}/js/jquery.countdown.min.js"></script>
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/jquery.countdown.min.js"></script> {% endaddtoblock %}
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/theme.js"></script> {% endaddtoblock %}
|
<script src="{{STATIC_URL}}/js/theme.js"></script>
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/index-slider.js" type="text/javascript" ></script>{% endaddtoblock %}
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
{% addtoblock "js" strip %} <script src="{{STATIC_URL}}js/bindWithDelay.js" type="text/javascript" ></script>{% endaddtoblock %}
|
<script src="{{STATIC_URL}}/js/index-slider.js" type="text/javascript"></script>
|
||||||
|
{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
{% addtoblock "js" %}
|
<script
|
||||||
|
src="{{STATIC_URL}}/js/bindWithDelay.js"
|
||||||
|
type="text/javascript"
|
||||||
|
></script>
|
||||||
|
{% endaddtoblock %} {% addtoblock "js" %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var cookieValue = null;
|
var cookieValue = null;
|
||||||
if (document.cookie && document.cookie != '') {
|
if (document.cookie && document.cookie != "") {
|
||||||
var cookies = document.cookie.split(';');
|
var cookies = document.cookie.split(";");
|
||||||
for (var i = 0; i < cookies.length; i++) {
|
for (var i = 0; i < cookies.length; i++) {
|
||||||
var cookie = jQuery.trim(cookies[i]);
|
var cookie = jQuery.trim(cookies[i]);
|
||||||
// Does this cookie string begin with the name we want?
|
// Does this cookie string begin with the name we want?
|
||||||
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
if (cookie.substring(0, name.length + 1) == name + "=") {
|
||||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
cookieValue = decodeURIComponent(
|
||||||
|
cookie.substring(name.length + 1),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
}
|
}
|
||||||
var csrftoken = getCookie('csrftoken');
|
var csrftoken = getCookie("csrftoken");
|
||||||
|
|
||||||
|
function csrfSafeMethod(method) {
|
||||||
function csrfSafeMethod(method) {
|
|
||||||
// these HTTP methods do not require CSRF protection
|
// these HTTP methods do not require CSRF protection
|
||||||
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method);
|
||||||
}
|
}
|
||||||
$.ajaxSetup({
|
$.ajaxSetup({
|
||||||
crossDomain: false, // obviates need for sameOrigin test
|
crossDomain: false, // obviates need for sameOrigin test
|
||||||
beforeSend: function(xhr, settings) {
|
beforeSend: function (xhr, settings) {
|
||||||
if (!csrfSafeMethod(settings.type)) {
|
if (!csrfSafeMethod(settings.type)) {
|
||||||
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,69 @@
|
|||||||
#from django.contrib import admin
|
from django.contrib import admin
|
||||||
#from eventplanner.models import Event, EventParticipation
|
|
||||||
#
|
from .models import Event, EventParticipation
|
||||||
#
|
|
||||||
#class EventParticipationInline(admin.TabularInline):
|
|
||||||
# model = EventParticipation
|
class EventParticipationInline(admin.TabularInline):
|
||||||
# extra = 1
|
"""Inline admin for event participations."""
|
||||||
# readonly_fields = ('user',)
|
|
||||||
# fields = ('user', 'status', 'comment',)
|
model = EventParticipation
|
||||||
# has_add_permission = lambda self, req: False
|
extra = 0
|
||||||
# has_delete_permission = lambda self, req, obj: False
|
readonly_fields = ("user",)
|
||||||
#
|
fields = ("user", "status", "comment")
|
||||||
# template = "eventplanner/admin_tabular.html"
|
|
||||||
#
|
def has_add_permission(self, request, obj=None):
|
||||||
#
|
return False
|
||||||
#class EventAdmin(admin.ModelAdmin):
|
|
||||||
# inlines = (EventParticipationInline,)
|
def has_delete_permission(self, request, obj=None):
|
||||||
#
|
return False
|
||||||
#
|
|
||||||
#admin.site.register(Event, EventAdmin)
|
|
||||||
|
@admin.register(Event)
|
||||||
|
class EventAdmin(admin.ModelAdmin):
|
||||||
|
"""Admin configuration for Event model."""
|
||||||
|
|
||||||
|
list_display = ("title", "type", "date", "time", "location")
|
||||||
|
list_filter = ("type", "date")
|
||||||
|
search_fields = ("short_desc", "location", "desc")
|
||||||
|
date_hierarchy = "date"
|
||||||
|
ordering = ("-date",)
|
||||||
|
|
||||||
|
inlines = (EventParticipationInline,)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": ("type", "short_desc", "date", "end_date"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Time",
|
||||||
|
{
|
||||||
|
"fields": ("time", "meeting_time"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Location",
|
||||||
|
{
|
||||||
|
"fields": ("location", "map_location"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Description",
|
||||||
|
{
|
||||||
|
"fields": ("desc",),
|
||||||
|
"classes": ("collapse",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(EventParticipation)
|
||||||
|
class EventParticipationAdmin(admin.ModelAdmin):
|
||||||
|
"""Admin configuration for EventParticipation model."""
|
||||||
|
|
||||||
|
list_display = ("event", "user", "status", "comment")
|
||||||
|
list_filter = ("status", "event__date")
|
||||||
|
search_fields = ("user__username", "event__short_desc", "comment")
|
||||||
|
raw_id_fields = ("event", "user")
|
||||||
|
|||||||
55
eventplanner/migrations/0001_initial.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Generated by Django 5.1.15 on 2026-03-30 19:15
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import location_field.models
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Event',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('type', models.CharField(choices=[('Reh', 'Rehearsal'), ('Conc', 'Concert'), ('Party', 'Party'), ('Travel', 'Travel'), ('Option', 'Option')], default='Reh', max_length=6, verbose_name='type')),
|
||||||
|
('short_desc', models.CharField(blank=True, max_length=100, null=True, verbose_name='Short Description')),
|
||||||
|
('location', models.TextField(blank=True, verbose_name='location')),
|
||||||
|
('map_location', location_field.models.PlainLocationField(based_field=models.TextField(blank=True, verbose_name='location'), blank=True, max_length=63, verbose_name='Location on map', zoom=7)),
|
||||||
|
('desc', models.TextField(blank=True, verbose_name='description')),
|
||||||
|
('date', models.DateField(verbose_name='date')),
|
||||||
|
('time', models.TimeField(blank=True, null=True, verbose_name='time')),
|
||||||
|
('meeting_time', models.TimeField(blank=True, null=True, verbose_name='meeting_time')),
|
||||||
|
('end_date', models.DateField(blank=True, null=True, verbose_name='End Date')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['date', 'time'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EventParticipation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('status', models.CharField(choices=[('?', '?'), ('Yes', 'Yes'), ('No', 'No'), ('-', '-')], default='?', max_length=3, verbose_name='status')),
|
||||||
|
('comment', models.CharField(blank=True, max_length=64, verbose_name='comment')),
|
||||||
|
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eventplanner.event', verbose_name='event')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('admin', 'Admin'), ('member', 'Member')),
|
||||||
|
'unique_together': {('event', 'user')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='participants',
|
||||||
|
field=models.ManyToManyField(through='eventplanner.EventParticipation', to=settings.AUTH_USER_MODEL, verbose_name='participants'),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
eventplanner/migrations/__init__.py
Normal file
@@ -1,9 +1,10 @@
|
|||||||
from django.db import models
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.contrib.auth.models import User, Permission
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Permission, User
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from location_field.models import PlainLocationField
|
from location_field.models import PlainLocationField
|
||||||
|
|
||||||
|
|
||||||
@@ -14,38 +15,53 @@ class NoNextEventException(Exception):
|
|||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
EVENT_TYPES = (
|
EVENT_TYPES = (
|
||||||
('Reh', _('Rehearsal')),
|
("Reh", _("Rehearsal")),
|
||||||
('Conc', _('Concert')),
|
("Conc", _("Concert")),
|
||||||
('Party', _('Party')),
|
("Party", _("Party")),
|
||||||
('Travel', _('Travel')),
|
("Travel", _("Travel")),
|
||||||
('Option', _('Option')),
|
("Option", _("Option")),
|
||||||
)
|
)
|
||||||
|
|
||||||
type = models.CharField(max_length=6, choices=EVENT_TYPES, default='Reh', verbose_name=_("type"))
|
type = models.CharField(
|
||||||
short_desc = models.CharField(null=True, max_length=100, blank=True, verbose_name=_("Short Description"))
|
max_length=6, choices=EVENT_TYPES, default="Reh", verbose_name=_("type")
|
||||||
|
)
|
||||||
|
short_desc = models.CharField(
|
||||||
|
null=True, max_length=100, blank=True, verbose_name=_("Short Description")
|
||||||
|
)
|
||||||
location = models.TextField(blank=True, verbose_name=_("location"))
|
location = models.TextField(blank=True, verbose_name=_("location"))
|
||||||
map_location = PlainLocationField(blank=True, based_field=location, zoom=7, verbose_name=_("Location on map"))
|
map_location = PlainLocationField(
|
||||||
|
blank=True, based_field=location, zoom=7, verbose_name=_("Location on map")
|
||||||
|
)
|
||||||
desc = models.TextField(blank=True, verbose_name=_("description"))
|
desc = models.TextField(blank=True, verbose_name=_("description"))
|
||||||
|
|
||||||
date = models.DateField(verbose_name=_("date"))
|
date = models.DateField(verbose_name=_("date"))
|
||||||
time = models.TimeField(null=True, blank=True, verbose_name=_("time"))
|
time = models.TimeField(null=True, blank=True, verbose_name=_("time"))
|
||||||
meeting_time = models.TimeField(null=True, blank=True, verbose_name=_("meeting_time"))
|
meeting_time = models.TimeField(
|
||||||
|
null=True, blank=True, verbose_name=_("meeting_time")
|
||||||
|
)
|
||||||
|
|
||||||
end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date"))
|
end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date"))
|
||||||
|
|
||||||
participants = models.ManyToManyField(User, through='EventParticipation', verbose_name=_("participants"))
|
participants = models.ManyToManyField(
|
||||||
|
User, through="EventParticipation", verbose_name=_("participants")
|
||||||
|
)
|
||||||
|
|
||||||
def __unicode__(self):
|
class Meta:
|
||||||
|
ordering = ["date", "time"]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# Call the "real" save() method
|
# Call the "real" save() method
|
||||||
super(Event, self).save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
# Create a "Don't Know" participation for each Musician
|
# Create a "Don't Know" participation for each Musician
|
||||||
for u in User.objects.all():
|
for u in User.objects.all():
|
||||||
if not u in self.participants.all():
|
if u not in self.participants.all():
|
||||||
EventParticipation.objects.create(event=self, user=u, status='-', comment='')
|
EventParticipation.objects.create(
|
||||||
|
event=self, user=u, status="-", comment=""
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
@@ -64,7 +80,7 @@ class Event(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def displaydatetime(self):
|
def displaydatetime(self):
|
||||||
if not self.displaytime == None:
|
if self.displaytime is not None:
|
||||||
return datetime.combine(self.date, self.displaytime)
|
return datetime.combine(self.date, self.displaytime)
|
||||||
else:
|
else:
|
||||||
return datetime.combine(self.date, datetime.min.time())
|
return datetime.combine(self.date, datetime.min.time())
|
||||||
@@ -73,13 +89,17 @@ class Event(models.Model):
|
|||||||
def getNextEvent(eventType="", includePreviousFromToday=True):
|
def getNextEvent(eventType="", includePreviousFromToday=True):
|
||||||
"""Return the next event, of the given type. If type is the empty string the next event is returned
|
"""Return the next event, of the given type. If type is the empty string the next event is returned
|
||||||
regardless of its type.
|
regardless of its type.
|
||||||
if includePreviousFromToday the nextEvent returned could also have been today with a startime < now """
|
if includePreviousFromToday the nextEvent returned could also have been today with a startime < now"""
|
||||||
|
|
||||||
if includePreviousFromToday:
|
if includePreviousFromToday:
|
||||||
if eventType == "":
|
if eventType == "":
|
||||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by('date')[:1]
|
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by(
|
||||||
|
"date"
|
||||||
|
)[:1]
|
||||||
else:
|
else:
|
||||||
nextEvents = Event.objects.filter(date__gte=datetime.now(), type=eventType).order_by('date')[:1]
|
nextEvents = Event.objects.filter(
|
||||||
|
date__gte=datetime.now(), type=eventType
|
||||||
|
).order_by("date")[:1]
|
||||||
|
|
||||||
if len(nextEvents) == 0:
|
if len(nextEvents) == 0:
|
||||||
raise NoNextEventException()
|
raise NoNextEventException()
|
||||||
@@ -89,11 +109,13 @@ class Event(models.Model):
|
|||||||
maximalNumberOfEventsOnSameDay = 4
|
maximalNumberOfEventsOnSameDay = 4
|
||||||
nextEvents = []
|
nextEvents = []
|
||||||
if eventType == "":
|
if eventType == "":
|
||||||
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by('date')[
|
nextEvents = Event.objects.filter(date__gte=datetime.now()).order_by(
|
||||||
:maximalNumberOfEventsOnSameDay]
|
"date"
|
||||||
|
)[:maximalNumberOfEventsOnSameDay]
|
||||||
else:
|
else:
|
||||||
nextEvents = Event.objects.filter(date__gte=datetime.now(), type=eventType).order_by('date')[
|
nextEvents = Event.objects.filter(
|
||||||
:maximalNumberOfEventsOnSameDay]
|
date__gte=datetime.now(), type=eventType
|
||||||
|
).order_by("date")[:maximalNumberOfEventsOnSameDay]
|
||||||
|
|
||||||
if len(nextEvents) == 0:
|
if len(nextEvents) == 0:
|
||||||
raise NoNextEventException()
|
raise NoNextEventException()
|
||||||
@@ -112,28 +134,38 @@ class Event(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class EventParticipation(models.Model):
|
class EventParticipation(models.Model):
|
||||||
OPTIONS = (('?', _('?')),
|
OPTIONS = (
|
||||||
('Yes', _('Yes')),
|
("?", _("?")),
|
||||||
('No', _('No')),
|
("Yes", _("Yes")),
|
||||||
('-', _('-'))
|
("No", _("No")),
|
||||||
|
("-", _("-")),
|
||||||
)
|
)
|
||||||
|
|
||||||
event = models.ForeignKey(Event, verbose_name=_("event"), on_delete=models.PROTECT)
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, verbose_name=_("event"))
|
||||||
user = models.ForeignKey(User, verbose_name=_("user"), on_delete=models.PROTECT)
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_("user"))
|
||||||
status = models.CharField(max_length=3, choices=OPTIONS, default='?', verbose_name=_("status"))
|
status = models.CharField(
|
||||||
|
max_length=3, choices=OPTIONS, default="?", verbose_name=_("status")
|
||||||
|
)
|
||||||
comment = models.CharField(max_length=64, blank=True, verbose_name=_("comment"))
|
comment = models.CharField(max_length=64, blank=True, verbose_name=_("comment"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("event", "user")
|
||||||
|
permissions = (
|
||||||
|
("admin", _("Admin")),
|
||||||
|
("member", _("Member")),
|
||||||
|
)
|
||||||
|
|
||||||
def get_username(self):
|
def get_username(self):
|
||||||
return self.user.username
|
return self.user.username
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
prev = EventParticipation.objects.filter(event=self.event, user=self.user)
|
prev = EventParticipation.objects.filter(event=self.event, user=self.user)
|
||||||
if len(prev) == 0:
|
if len(prev) == 0:
|
||||||
super(EventParticipation, self).save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
prev = prev[0]
|
prev = prev[0]
|
||||||
if prev.status != self.status or prev.comment != self.comment:
|
if prev.status != self.status or prev.comment != self.comment:
|
||||||
super(EventParticipation, self).save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def hasUserSetParticipationForAllEvents(user):
|
def hasUserSetParticipationForAllEvents(user):
|
||||||
@@ -142,7 +174,7 @@ class EventParticipation(models.Model):
|
|||||||
|
|
||||||
futurePart = EventParticipation.objects.filter(event__date__gte=datetime.now())
|
futurePart = EventParticipation.objects.filter(event__date__gte=datetime.now())
|
||||||
|
|
||||||
notYetEntered = futurePart.filter(user=user).filter(status='-')
|
notYetEntered = futurePart.filter(user=user).filter(status="-")
|
||||||
if len(notYetEntered) > 0:
|
if len(notYetEntered) > 0:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
@@ -150,30 +182,27 @@ class EventParticipation(models.Model):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isMember(user):
|
def isMember(user):
|
||||||
return user.has_perm('eventplanner.member')
|
return user.has_perm("eventplanner.member")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isAdmin(user):
|
def isAdmin(user):
|
||||||
return user.has_perm('eventplanner.admin')
|
return user.has_perm("eventplanner.admin")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def members():
|
def members():
|
||||||
perm = Permission.objects.get(codename='member')
|
perm = Permission.objects.get(codename="member")
|
||||||
f = User.objects.filter(Q(groups__permissions=perm) | Q(user_permissions=perm)).distinct()
|
f = User.objects.filter(
|
||||||
return f.order_by('musician__position')
|
Q(groups__permissions=perm) | Q(user_permissions=perm)
|
||||||
|
).distinct()
|
||||||
|
return f.order_by("musician__position")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_or_create(user, event):
|
def get_or_create(user, event):
|
||||||
try:
|
try:
|
||||||
result = EventParticipation.objects.get(event=event, user=user)
|
result = EventParticipation.objects.get(event=event, user=user)
|
||||||
except EventParticipation.DoesNotExist:
|
except EventParticipation.DoesNotExist:
|
||||||
result = EventParticipation.objects.create(event=event, user=user, status='-', comment='')
|
result = EventParticipation.objects.create(
|
||||||
|
event=event, user=user, status="-", comment=""
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ("event", "user")
|
|
||||||
permissions = (
|
|
||||||
("admin", _("Admin")),
|
|
||||||
("member", _("Member")),
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import EventParticipation, Event
|
|
||||||
|
from .models import Event, EventParticipation
|
||||||
|
|
||||||
|
|
||||||
class ParticipationSerializer(serializers.ModelSerializer):
|
class ParticipationSerializer(serializers.ModelSerializer):
|
||||||
event = serializers.PrimaryKeyRelatedField(many=False, read_only=False, queryset=Event.objects.all())
|
event = serializers.PrimaryKeyRelatedField(queryset=Event.objects.all())
|
||||||
user = serializers.Field(source='get_username')
|
user = serializers.CharField(source="get_username", read_only=True)
|
||||||
status = serializers.CharField(source='status', required=False)
|
status = serializers.CharField(required=False)
|
||||||
|
|
||||||
def get_identity(self, data):
|
|
||||||
""" This hook is required for bulk update. """
|
|
||||||
try:
|
|
||||||
return data.get('event', None), data.get('user')
|
|
||||||
except AttributeError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = EventParticipation
|
model = EventParticipation
|
||||||
fields = ('event', 'user', 'status', 'comment')
|
fields = ("event", "user", "status", "comment")
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
# Remove the get_username source field as it's read-only
|
||||||
|
validated_data.pop("get_username", None)
|
||||||
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
instance.status = validated_data.get("status", instance.status)
|
||||||
|
instance.comment = validated_data.get("comment", instance.comment)
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|||||||
@@ -1,50 +1,58 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from .models import Event, EventParticipation, NoNextEventException
|
|
||||||
from musicians.models import Musician
|
from musicians.models import Musician
|
||||||
|
|
||||||
|
from .models import Event, EventParticipation, NoNextEventException
|
||||||
|
|
||||||
|
|
||||||
def addEventCountdownForNextEventToContext(context, username, eventType=""):
|
def addEventCountdownForNextEventToContext(context, username, eventType=""):
|
||||||
"""Returns an object that has to be added to the render context on the page where the countdown
|
"""Returns an object that has to be added to the render context on the page where the countdown
|
||||||
should be displayed . The username is required to also supply participation information."""
|
should be displayed. The username is required to also supply participation information."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nextEvent = Event.getNextEvent(eventType, False)
|
nextEvent = Event.getNextEvent(eventType, False)
|
||||||
except NoNextEventException:
|
except NoNextEventException:
|
||||||
return
|
return
|
||||||
|
|
||||||
countdown = dict()
|
countdown = {}
|
||||||
|
|
||||||
if EventParticipation.isMember(username):
|
if EventParticipation.isMember(username):
|
||||||
part = EventParticipation.objects.filter(user=username).filter(event=nextEvent)
|
part = EventParticipation.objects.filter(user=username).filter(event=nextEvent)
|
||||||
countdown['participation'] = part[0].status
|
if part.exists():
|
||||||
|
countdown["participation"] = part[0].status
|
||||||
|
|
||||||
eventTime = nextEvent.displaydatetime
|
eventTime = nextEvent.displaydatetime
|
||||||
countdown['event'] = nextEvent
|
countdown["event"] = nextEvent
|
||||||
countdown['epoch'] = int((eventTime - datetime.now()).total_seconds() * 1000)
|
countdown["epoch"] = int((eventTime - datetime.now()).total_seconds() * 1000)
|
||||||
|
|
||||||
context["countdown"] = countdown
|
context["countdown"] = countdown
|
||||||
|
|
||||||
|
|
||||||
def addEventRouteForNextEventToContext(context, username, eventType=""):
|
def addEventRouteForNextEventToContext(context, username, eventType=""):
|
||||||
"""Returns an object that has to be added to the render context on the page where the route
|
"""Returns an object that has to be added to the render context on the page where the route
|
||||||
should be displayed . The starting address of the route will be the home of the specified user"""
|
should be displayed. The starting address of the route will be the home of the specified user"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nextEvent = Event.getNextEvent(eventType, True)
|
nextEvent = Event.getNextEvent(eventType, True)
|
||||||
except NoNextEventException:
|
except NoNextEventException:
|
||||||
return
|
return
|
||||||
|
|
||||||
routeInfo = dict()
|
routeInfo = {}
|
||||||
|
|
||||||
routeInfo['event'] = nextEvent
|
routeInfo["event"] = nextEvent
|
||||||
|
|
||||||
musician = Musician.objects.get(user=username);
|
try:
|
||||||
routeInfo['origin'] = musician.street + ", " + str(musician.zip_code) + " " + musician.city
|
musician = Musician.objects.get(user=username)
|
||||||
|
routeInfo["origin"] = (
|
||||||
|
musician.street + ", " + str(musician.zip_code) + " " + musician.city
|
||||||
|
)
|
||||||
|
except Musician.DoesNotExist:
|
||||||
|
routeInfo["origin"] = ""
|
||||||
|
|
||||||
if nextEvent.map_location:
|
if nextEvent.map_location:
|
||||||
# map_location has format "lat,longitute,zoomlevel"
|
# map_location has format "lat,longitude,zoomlevel"
|
||||||
routeInfo['destination'] = ",".join(nextEvent.map_location.split(",")[:2])
|
routeInfo["destination"] = ",".join(nextEvent.map_location.split(",")[:2])
|
||||||
else:
|
else:
|
||||||
routeInfo['destination'] = nextEvent.location
|
routeInfo["destination"] = nextEvent.location
|
||||||
|
|
||||||
context["route"] = routeInfo
|
context["route"] = routeInfo
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% load i18n admin_static admin_modify %}
|
{% load i18n static admin_modify %}
|
||||||
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
||||||
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
||||||
{{ inline_admin_formset.formset.management_form }}
|
{{ inline_admin_formset.formset.management_form }}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
{% load sekizai_tags staticfiles %}
|
{% load sekizai_tags static %} {% addtoblock "css" strip %}<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="{{STATIC_URL}}/css/lib/animate.css"
|
||||||
{% addtoblock "css" strip %}<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/lib/animate.css" media="screen, projection">{% endaddtoblock %}
|
media="screen, projection"
|
||||||
{% addtoblock "css" strip %}<link rel="stylesheet" href="{{STATIC_URL}}css/coming-soon.css" type="text/css" media="screen" />{% endaddtoblock %}
|
/>{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||||
|
rel="stylesheet"
|
||||||
{% if countdown %}
|
href="{{STATIC_URL}}/css/coming-soon.css"
|
||||||
|
type="text/css"
|
||||||
|
media="screen"
|
||||||
|
/>{% endaddtoblock %} {% if countdown %} {% addtoblock "js" %}
|
||||||
{% addtoblock "js" %}
|
<script type="text/javascript">
|
||||||
<script type="text/javascript">
|
|
||||||
$(function () {
|
$(function () {
|
||||||
function callback(event) {
|
function callback(event) {
|
||||||
$this = $(this);
|
$this = $(this);
|
||||||
@@ -27,48 +26,41 @@
|
|||||||
}
|
}
|
||||||
$('div#clock').countdown(new Date().valueOf() + {{ countdown.epoch }} , callback);
|
$('div#clock').countdown(new Date().valueOf() + {{ countdown.epoch }} , callback);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="coming_soon">
|
<div id="coming_soon">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="span6 text">
|
<div class="span6 text">
|
||||||
<h4>Der nächste Termin:</h4>
|
<h4>Der nächste Termin:</h4>
|
||||||
<p>
|
<p>
|
||||||
{{countdown.event.title}} am {{countdown.event.date | date:"D, d.m.y" }}
|
{{countdown.event.title}} am {{countdown.event.date | date:"D, d.m.y" }} {% if coundown.event.displaytime %} um
|
||||||
{% if coundown.event.displaytime %} um {{countdown.event.displaytime | time:"H:i" }} Uhr {% endif %}
|
{{countdown.event.displaytime | time:"H:i" }} Uhr {% endif %} {% if coundown.event.location %} in
|
||||||
{% if coundown.event.location %} in <em>{{countdown.event.location}} </em> {% endif %}
|
<em>{{countdown.event.location}} </em> {% endif %}
|
||||||
|
|
||||||
<br/>
|
<br />
|
||||||
{% if 'participation' in countdown %}
|
{% if 'participation' in countdown %} {% if countdown.participation == "?"%} Du hast dich noch nicht
|
||||||
{% if countdown.participation == "?" %}
|
für diesen Termin eingetragen! {% elif countdown.participation == "Yes"%} Du hast für diesen
|
||||||
Du hast dich noch nicht für diesen Termin eingetragen!
|
Termin zugesagt. {% elif countdown.participation == "No" %}
|
||||||
{% elif countdown.participation == "Yes" %}
|
Du hast für diesen Termin abgesagt. {%endif %} {% endif %}
|
||||||
Du hast für diesen Termin zugesagt.
|
|
||||||
{% elif countdown.participation == "No" %}
|
|
||||||
Du hast für diesen Termin abgesagt.
|
|
||||||
{%endif %}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="span6 count" id="clock">
|
<div class="span6 count" id="clock">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="circle"> <span id="days"></span> </div>
|
<div class="circle"><span id="days"></span></div>
|
||||||
<p>Tage</p>
|
<p>Tage</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="circle"> <span id="hours"></span> </div>
|
<div class="circle"><span id="hours"></span></div>
|
||||||
<p>Stunden</p>
|
<p>Stunden</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="circle"> <span id="minutes"></span> </div>
|
<div class="circle"><span id="minutes"></span></div>
|
||||||
<p>Minuten</p>
|
<p>Minuten</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="box last">
|
<div class="box last">
|
||||||
<div class="circle"> <span id="seconds"></span> </div>
|
<div class="circle"><span id="seconds"></span></div>
|
||||||
<p>Sekunden</p>
|
<p>Sekunden</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
{% extends "website/base.html" %}
|
{% extends "website/base.html" %} {% load sekizai_tags static %} {% load crispy_forms_tags %} {% block content %} {{ form.media }}
|
||||||
|
|
||||||
{% load sekizai_tags staticfiles %}
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -14,39 +6,48 @@
|
|||||||
<h3>Termin bearbeiten</h3>
|
<h3>Termin bearbeiten</h3>
|
||||||
|
|
||||||
{% crispy form %}
|
{% crispy form %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Datepicker -->
|
<!-- Datepicker -->
|
||||||
|
|
||||||
{% addtoblock "css" strip %}
|
{% addtoblock "css" strip %}<link
|
||||||
<link rel="stylesheet" href="{{STATIC_URL}}css/jquery-ui-1.12.1.min.css" type="text/css" media="screen" />
|
rel="stylesheet"
|
||||||
{% endaddtoblock %}
|
href="{{STATIC_URL}}/css/datepicker.css"
|
||||||
|
type="text/css"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
|
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{STATIC_URL}}/css/timepicker.css"
|
||||||
|
type="text/css"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
|
{% endaddtoblock %} {% addtoblock "css" strip %}<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{STATIC_URL}}/css/jquery-ui-1.8.21.custom.css"
|
||||||
|
type="text/css"
|
||||||
|
media="screen"
|
||||||
|
/>
|
||||||
|
{% endaddtoblock %} {% addtoblock "js" %}
|
||||||
|
<script src="{{STATIC_URL}}/js/bootstrap-timepicker.js"></script>
|
||||||
|
<script src="{{STATIC_URL}}/js/bootstrap-datepicker.js"></script>
|
||||||
|
<script src="{{STATIC_URL}}/js/bootstrap-datepicker.de.js"></script>
|
||||||
|
|
||||||
|
|
||||||
{% addtoblock "js" %}
|
|
||||||
<script src="{{STATIC_URL}}js/jquery-ui-1.12.1.min.js"></script>
|
|
||||||
<script src="{{STATIC_URL}}js/bootstrap-datepicker.de.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
$.datepicker.setDefaults(
|
$('.dateinput').datepicker({
|
||||||
$.extend(
|
format: "dd.mm.yyyy",
|
||||||
{'dateFormat':'dd-mm-yy'},
|
weekStart: 1,
|
||||||
$.datepicker.regional['de']
|
todayBtn: "linked",
|
||||||
)
|
language: "de",
|
||||||
);
|
todayHighlight: true,
|
||||||
|
startDate: "{% now "SHORT_DATE_FORMAT" %}",
|
||||||
|
});
|
||||||
|
|
||||||
$('.dateinput').datepicker();
|
|
||||||
|
|
||||||
/*
|
|
||||||
$('.timeinput').addClass('input-small').wrap('<div class="input-append bootstrap-timepicker">')
|
$('.timeinput').addClass('input-small').wrap('<div class="input-append bootstrap-timepicker">')
|
||||||
$(".input-append").append( '<span class="add-on"><i class="icon-time"></i></span>' )
|
$(".input-append").append( '<span class="add-on"><i class="icon-time"></i></span>' )
|
||||||
|
|
||||||
@@ -56,7 +57,6 @@ $(document).ready(function(){
|
|||||||
showMeridian: false,
|
showMeridian: false,
|
||||||
defaultTime: false
|
defaultTime: false
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
$('form').submit(function() {
|
$('form').submit(function() {
|
||||||
if ( $("#id_type").val() != "Option" && $("#id_location").val().trim() == "" ) {
|
if ( $("#id_type").val() != "Option" && $("#id_location").val().trim() == "" ) {
|
||||||
@@ -126,13 +126,6 @@ $(document).ready(function(){
|
|||||||
} );
|
} );
|
||||||
onTypeChange( $("#id_type").val() );
|
onTypeChange( $("#id_type").val() );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
</script>
|
</script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %} {% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "website/base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% load sekizai_tags staticfiles %}
|
{% load sekizai_tags static %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
|
|
||||||
request = $.ajax( {
|
request = $.ajax( {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: "{% url 'event_api' %}",
|
url: "{% url 'eventplanner:event_api' %}",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
data: JSON.stringify(putObject),
|
data: JSON.stringify(putObject),
|
||||||
success: function() { $("#saving").html("Ok"); },
|
success: function() { $("#saving").html("Ok"); },
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
|
|
||||||
$.ajax( {
|
$.ajax( {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: "{% url 'event_api' %}",
|
url: "{% url 'eventplanner:event_api' %}",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
data: JSON.stringify(putObject),
|
data: JSON.stringify(putObject),
|
||||||
success: function() { $("#saving").html("Ok"); }
|
success: function() { $("#saving").html("Ok"); }
|
||||||
@@ -181,6 +181,14 @@
|
|||||||
|
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Es gibt jetzt einen vierten Zustand "nicht eingetragen", angezeigt durch nur
|
||||||
|
blaue Buttons.<br>
|
||||||
|
|
||||||
|
"?" bedeutet jetzt: Ich habe mich eingetragen weiss aber noch nicht ob ich komme.
|
||||||
|
Bitte nur selten benutzen!
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<table id="eventTable" class="table table-striped">
|
<table id="eventTable" class="table table-striped">
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "website/base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% load sekizai_tags staticfiles %}
|
{% load sekizai_tags static %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
$.ajax( {
|
$.ajax( {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: "{% url 'event_api' %}",
|
url: "{% url 'eventplanner:event_api' %}",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
data: JSON.stringify(arr)
|
data: JSON.stringify(arr)
|
||||||
});
|
});
|
||||||
@@ -143,6 +143,13 @@
|
|||||||
|
|
||||||
<h2>Termine</h2>
|
<h2>Termine</h2>
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Es gibt jetzt einen vierten Zustand "nicht eingetragen", angezeigt durch "-".<br>
|
||||||
|
|
||||||
|
"?" bedeutet jetzt: Ich habe mich eingetragen weiss aber noch nicht ob ich komme.
|
||||||
|
Bitte nur selten benutzen!
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,17 @@
|
|||||||
{% comment %}
|
{% comment %} Displays google map with directions to next conert Context:
|
||||||
Displays google map with directions to next conert
|
Coordinates or textual adresses: {{route.origin}} {{route.destination}} Event
|
||||||
|
object: {{route.event}} {% endcomment %} {% load sekizai_tags static %}
|
||||||
Context:
|
{% if route %} {% addtoblock "css" strip %}<link
|
||||||
Coordinates or textual adresses:
|
rel="stylesheet"
|
||||||
{{route.origin}}
|
href="{{STATIC_URL}}/css/concert_route.css"
|
||||||
{{route.destination}}
|
type="text/css"
|
||||||
Event object:
|
media="screen"
|
||||||
{{route.event}}
|
/>{% endaddtoblock %} {% addtoblock "js" strip %}
|
||||||
{% endcomment %}
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="//maps.google.com/maps/api/js?sensor=false&language=de"
|
||||||
|
></script>
|
||||||
|
{% endaddtoblock %} {% addtoblock "js" %}
|
||||||
{% load sekizai_tags staticfiles %}
|
|
||||||
|
|
||||||
|
|
||||||
{% if route %}
|
|
||||||
|
|
||||||
{% addtoblock "css" strip %}<link rel="stylesheet" href="{{STATIC_URL}}css/concert_route.css" type="text/css" media="screen" />{% endaddtoblock %}
|
|
||||||
{% addtoblock "js" strip %}<script type="text/javascript" src="//maps.google.com/maps/api/js?key={{GOOGLE_MAPS_API_KEY}}&sensor=false&language=de"></script>{% endaddtoblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% addtoblock "js" %}
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function OpenWindowControl(controlDiv, map) {
|
function OpenWindowControl(controlDiv, map) {
|
||||||
@@ -163,9 +153,6 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="concert_route">
|
<div id="concert_route">
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<div id="map_box" class="row map">
|
<div id="map_box" class="row map">
|
||||||
@@ -186,23 +173,43 @@
|
|||||||
</table>
|
</table>
|
||||||
-->
|
-->
|
||||||
<table class="table table-striped table-condensed">
|
<table class="table table-striped table-condensed">
|
||||||
<tr><td>Ort: </td> <td> {{route.event.location}} </td> </tr>
|
<tr>
|
||||||
<tr><td>Datum: </td> <td> {{route.event.date | date:"D, d.m.y" }} </td> </tr>
|
<td>Ort:</td>
|
||||||
<tr><td>Uhrzeit: </td> <td> {{route.event.time | time:"H:i" }} Uhr </td> </tr>
|
<td>{{route.event.location}}</td>
|
||||||
{% if route.event.meeting_time %} <tr><td>Treffen um: </td> <td> {{route.event.meeting_time | time:"H:i" }} Uhr </td> </tr> {% endif %}
|
</tr>
|
||||||
<tr> <td> Fahrzeit:</td> <td> <span id="route_duration"></span> </td> </tr>
|
<tr>
|
||||||
<tr> <td> Strecke: </td> <td> <span id="route_distance"></span> </td> </tr>
|
<td>Datum:</td>
|
||||||
|
<td>{{route.event.date | date:"D, d.m.y" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Uhrzeit:</td>
|
||||||
|
<td>{{route.event.time | time:"H:i" }} Uhr</td>
|
||||||
|
</tr>
|
||||||
|
{% if route.event.meeting_time %}
|
||||||
|
<tr>
|
||||||
|
<td>Treffen um:</td>
|
||||||
|
<td>
|
||||||
|
{{route.event.meeting_time | time:"H:i" }}
|
||||||
|
Uhr
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td>Fahrzeit:</td>
|
||||||
|
<td><span id="route_duration"></span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Strecke:</td>
|
||||||
|
<td><span id="route_distance"></span></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<a class="btn">Schliessen</a>
|
||||||
|
</div>
|
||||||
<a class="btn" >Schliessen</a>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
from django.conf.urls import url
|
from django.urls import path, re_path
|
||||||
from django.contrib.auth.decorators import permission_required
|
|
||||||
from eventplanner.views import events_grid, eventplanning, event_api, EventUpdate, EventCreate, deleteEvent
|
from . import views
|
||||||
|
|
||||||
|
app_name = "eventplanner"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', eventplanning),
|
path("", views.eventplanning, name="eventplanning"),
|
||||||
url(r'^grid$', events_grid),
|
path("grid/", views.events_grid, name="events_grid"),
|
||||||
url(r'^planning$', eventplanning),
|
path("delete/<int:pk>/", views.deleteEvent, name="delete_event"),
|
||||||
url(r'^(?P<pk>\d+)$', permission_required('eventplanner.change_event')(EventUpdate.as_view())),
|
path("event/<int:pk>/", views.EventUpdate.as_view(), name="event_update"),
|
||||||
url(r'^add$', permission_required('eventplanner.add_event')(EventCreate.as_view())),
|
path("event/create/", views.EventCreate.as_view(), name="event_create"),
|
||||||
url(r'^(?P<pk>\d+)/delete$', permission_required('eventplanner.delete_event')(deleteEvent)),
|
# API endpoints
|
||||||
url(r'^api/', event_api, name="event_api"),
|
path("api/", views.event_api, name="event_api"),
|
||||||
url(r'^api/(\w+)/$', event_api, name="event_api_per_user"),
|
path("api/<str:username>/", views.event_api, name="event_api_user"),
|
||||||
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event"),
|
path(
|
||||||
|
"api/<str:username>/<int:eventId>/",
|
||||||
|
views.event_api,
|
||||||
|
name="event_api_user_event",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
from django.shortcuts import render, redirect
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.forms.models import ModelForm
|
|
||||||
from django.forms import TextInput, TimeInput
|
|
||||||
|
|
||||||
from .models import Event, EventParticipation
|
|
||||||
|
|
||||||
from .serializers import ParticipationSerializer
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from rest_framework.decorators import api_view
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework import status
|
|
||||||
|
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Submit
|
from crispy_forms.layout import Submit
|
||||||
|
from django.forms import TextInput
|
||||||
|
from django.forms.models import ModelForm
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from location_field.widgets import LocationWidget
|
||||||
|
|
||||||
|
from .models import Event, EventParticipation
|
||||||
|
from .serializers import ParticipationSerializer
|
||||||
|
|
||||||
# ---------------------------------------- API ---------------------------------------------------------
|
# ---------------------------------------- API ---------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@api_view(['GET', 'PUT'])
|
@api_view(["GET", "PUT"])
|
||||||
def event_api(request, username=None, eventId=None):
|
def event_api(request, username=None, eventId=None):
|
||||||
try:
|
try:
|
||||||
participationQs = EventParticipation.objects.filter(event__date__gte=datetime.date.today())
|
participationQs = EventParticipation.objects.filter(
|
||||||
|
event__date__gte=datetime.date.today()
|
||||||
|
)
|
||||||
if username:
|
if username:
|
||||||
participationQs = EventParticipation.objects.filter(user__username=username)
|
participationQs = EventParticipation.objects.filter(user__username=username)
|
||||||
if eventId:
|
if eventId:
|
||||||
@@ -31,28 +32,34 @@ def event_api(request, username=None, eventId=None):
|
|||||||
except EventParticipation.DoesNotExist:
|
except EventParticipation.DoesNotExist:
|
||||||
return HttpResponse(status=404)
|
return HttpResponse(status=404)
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == "GET":
|
||||||
serializer = ParticipationSerializer(participationQs)
|
serializer = ParticipationSerializer(participationQs, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
elif request.method == 'PUT':
|
elif request.method == "PUT":
|
||||||
serializer = ParticipationSerializer(participationQs, data=request.DATA, many=True)
|
serializer = ParticipationSerializer(data=request.data, many=True)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
for serializedObject in serializer.object:
|
for item in serializer.validated_data:
|
||||||
if not (EventParticipation.isMember(request.user) or EventParticipation.isAdmin(request.user)):
|
event = item.get("event")
|
||||||
|
user_obj = item.get("user")
|
||||||
|
if not (
|
||||||
|
EventParticipation.isMember(request.user)
|
||||||
|
or EventParticipation.isAdmin(request.user)
|
||||||
|
):
|
||||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||||
if serializedObject.user != request.user:
|
if user_obj != request.user:
|
||||||
if not EventParticipation.isAdmin(request.user):
|
if not EventParticipation.isAdmin(request.user):
|
||||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
else:
|
else:
|
||||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------ Normal Views ----------------------------------------------------
|
# ------------------------------------ Normal Views ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def eventplanning(request):
|
def eventplanning(request):
|
||||||
"""
|
"""
|
||||||
View for a specific user, to edit his events
|
View for a specific user, to edit his events
|
||||||
@@ -62,27 +69,33 @@ def eventplanning(request):
|
|||||||
return events_grid(request)
|
return events_grid(request)
|
||||||
|
|
||||||
# All events in the future sorted by date
|
# All events in the future sorted by date
|
||||||
all_future_events = list(Event.objects.filter(date__gte=datetime.date.today()).order_by('date'))
|
all_future_events = list(
|
||||||
|
Event.objects.filter(date__gte=datetime.date.today()).order_by("date")
|
||||||
|
)
|
||||||
|
|
||||||
for e in all_future_events:
|
for e in all_future_events:
|
||||||
e.participation = EventParticipation.get_or_create(event=e, user=request.user)
|
e.participation = EventParticipation.get_or_create(event=e, user=request.user)
|
||||||
|
|
||||||
context = {'events': all_future_events}
|
context = {"events": all_future_events}
|
||||||
return render(request, 'eventplanner/eventplanning_view.html', context)
|
return render(request, "eventplanner/eventplanning_view.html", context)
|
||||||
|
|
||||||
|
|
||||||
def events_grid(request):
|
def events_grid(request):
|
||||||
usernames = [u.username for u in EventParticipation.members()]
|
usernames = [u.username for u in EventParticipation.members()]
|
||||||
|
|
||||||
all_future_events = list(Event.objects.filter(date__gte=datetime.date.today()).order_by('date'))
|
all_future_events = list(
|
||||||
|
Event.objects.filter(date__gte=datetime.date.today()).order_by("date")
|
||||||
|
)
|
||||||
|
|
||||||
for e in all_future_events:
|
for e in all_future_events:
|
||||||
e.participation = [EventParticipation.get_or_create(event=e, user=u) for u in EventParticipation.members()]
|
e.participation = [
|
||||||
|
EventParticipation.get_or_create(event=e, user=u)
|
||||||
|
for u in EventParticipation.members()
|
||||||
|
]
|
||||||
|
|
||||||
context = {'events': all_future_events,
|
context = {"events": all_future_events, "usernames": usernames}
|
||||||
'usernames': usernames}
|
|
||||||
|
|
||||||
return render(request, 'eventplanner/events_grid.html', context)
|
return render(request, "eventplanner/events_grid.html", context)
|
||||||
|
|
||||||
|
|
||||||
def deleteEvent(request, pk):
|
def deleteEvent(request, pk):
|
||||||
@@ -93,27 +106,30 @@ def deleteEvent(request, pk):
|
|||||||
# ------------------------------------ Detail Views ----------------------------------------------------
|
# ------------------------------------ Detail Views ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
from django.views.generic.edit import UpdateView, CreateView
|
|
||||||
|
|
||||||
from location_field.widgets import LocationWidget
|
|
||||||
|
|
||||||
|
|
||||||
class EventForm(ModelForm):
|
class EventForm(ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_class = 'form-horizontal'
|
self.helper.form_class = "form-horizontal"
|
||||||
self.helper.add_input(Submit('submit', 'Speichern'))
|
self.helper.add_input(Submit("submit", "Speichern"))
|
||||||
super(EventForm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Event
|
model = Event
|
||||||
fields = ['type', 'short_desc', 'date', 'end_date', 'time', 'meeting_time', 'location', 'map_location',
|
fields = [
|
||||||
'desc', ]
|
"type",
|
||||||
|
"short_desc",
|
||||||
|
"date",
|
||||||
|
"end_date",
|
||||||
|
"time",
|
||||||
|
"meeting_time",
|
||||||
|
"location",
|
||||||
|
"map_location",
|
||||||
|
"desc",
|
||||||
|
]
|
||||||
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'time': TimeInput(format='%H:%M'),
|
"location": TextInput(),
|
||||||
'location': TextInput(),
|
"map_location": LocationWidget(),
|
||||||
'map_location': LocationWidget(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -121,11 +137,11 @@ class EventUpdate(UpdateView):
|
|||||||
form_class = EventForm
|
form_class = EventForm
|
||||||
model = Event
|
model = Event
|
||||||
template_name_suffix = "_update_form"
|
template_name_suffix = "_update_form"
|
||||||
success_url = '.'
|
success_url = "."
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['viewtype'] = "update"
|
context["viewtype"] = "update"
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@@ -133,9 +149,9 @@ class EventCreate(CreateView):
|
|||||||
form_class = EventForm
|
form_class = EventForm
|
||||||
model = Event
|
model = Event
|
||||||
template_name_suffix = "_update_form"
|
template_name_suffix = "_update_form"
|
||||||
success_url = '.'
|
success_url = "."
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['viewtype'] = "create"
|
context["viewtype"] = "create"
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# eventplanner_gcal app
|
||||||
|
# Signals are loaded in apps.py AppConfig.ready()
|
||||||
|
default_app_config = "eventplanner_gcal.apps.EventplannerGcalConfig"
|
||||||
|
|||||||
14
eventplanner_gcal/apps.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class EventplannerGcalConfig(AppConfig):
|
||||||
|
"""App configuration for eventplanner_gcal."""
|
||||||
|
|
||||||
|
name = "eventplanner_gcal"
|
||||||
|
verbose_name = "Event Planner Google Calendar Integration"
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
"""Import signal handlers when the app is ready."""
|
||||||
|
# Import signals to register them
|
||||||
|
from . import signals # noqa: F401
|
||||||
@@ -1,70 +1,112 @@
|
|||||||
import logging
|
"""
|
||||||
import httplib2
|
Google Calendar synchronization module.
|
||||||
|
|
||||||
|
This module handles synchronization between the local event database
|
||||||
|
and Google Calendar, including push notifications for real-time updates.
|
||||||
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from eventplanner.models import Event, EventParticipation
|
from eventplanner.models import Event, EventParticipation
|
||||||
from eventplanner_gcal.models import GCalMapping, GCalPushChannel, UserGCalCoupling
|
from eventplanner_gcal.models import GCalMapping, GCalPushChannel, UserGCalCoupling
|
||||||
# noinspection PyUnresolvedReferences,PyUnresolvedReferences
|
|
||||||
from apiclient.http import BatchHttpRequest
|
|
||||||
from builtins import str as text # python2 and python3
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Module-level service object cache
|
||||||
|
_service_object = None
|
||||||
|
|
||||||
# ---------------------------------- Authentication using oauth2 -----------------------------------------------------
|
|
||||||
|
|
||||||
def create_gcal_service_object():
|
def create_gcal_service_object():
|
||||||
"""Creates a Google API service object. This object is required whenever a Google API call is made"""
|
"""
|
||||||
from oauth2client.file import Storage
|
Creates a Google API service object.
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
from apiclient.discovery import build
|
|
||||||
|
|
||||||
gcal_settings = settings.GCAL_COUPLING
|
This object is required whenever a Google API call is made.
|
||||||
|
Uses the new google-auth library instead of oauth2client.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
|
||||||
storage = Storage(gcal_settings['credentials_file'])
|
from google.auth.transport.requests import Request
|
||||||
credentials = storage.get()
|
from google.oauth2.credentials import Credentials
|
||||||
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||||
logger.debug("Credentials", credentials)
|
from googleapiclient.discovery import build
|
||||||
if credentials is None or credentials.invalid is True:
|
except ImportError as e:
|
||||||
# flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
|
logger.error(f"Required Google API libraries not installed: {e}")
|
||||||
logger.error("Unable to initialize Google Calendar coupling. Check your settings!")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
http = httplib2.Http()
|
gcal_settings = settings.GCAL_COUPLING
|
||||||
http = credentials.authorize(http)
|
credentials_file = gcal_settings["credentials_file"]
|
||||||
res = build(serviceName='calendar', version='v3',
|
|
||||||
http=http, developerKey=gcal_settings['developerKey'])
|
|
||||||
|
|
||||||
if res is None:
|
creds = None
|
||||||
logger.error("Authentication at Google API failed. Check your settings!")
|
|
||||||
return res
|
# Try to load existing credentials
|
||||||
|
if os.path.exists(credentials_file):
|
||||||
|
try:
|
||||||
|
with open(credentials_file, "rb") as token:
|
||||||
|
creds = pickle.load(token)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not load credentials from {credentials_file}: {e}")
|
||||||
|
|
||||||
|
# Check if credentials are valid
|
||||||
|
if creds and creds.expired and creds.refresh_token:
|
||||||
|
try:
|
||||||
|
creds.refresh(Request())
|
||||||
|
# Save refreshed credentials
|
||||||
|
with open(credentials_file, "wb") as token:
|
||||||
|
pickle.dump(creds, token)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to refresh credentials: {e}")
|
||||||
|
creds = None
|
||||||
|
|
||||||
|
if not creds or not creds.valid:
|
||||||
|
logger.error(
|
||||||
|
"Invalid or missing Google Calendar credentials. "
|
||||||
|
"Please run the credential setup process."
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
service = build("calendar", "v3", credentials=creds)
|
||||||
|
return service
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to build Google Calendar service: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_service_object():
|
def get_service_object():
|
||||||
if get_service_object.__serviceObject is None:
|
"""Get or create the Google Calendar service object."""
|
||||||
get_service_object.__serviceObject = create_gcal_service_object()
|
global _service_object
|
||||||
|
if _service_object is None:
|
||||||
return get_service_object.__serviceObject
|
_service_object = create_gcal_service_object()
|
||||||
|
return _service_object
|
||||||
|
|
||||||
|
|
||||||
get_service_object.__serviceObject = None
|
def reset_service_object():
|
||||||
|
"""Reset the cached service object (useful for testing or credential refresh)."""
|
||||||
|
global _service_object
|
||||||
|
_service_object = None
|
||||||
|
|
||||||
|
|
||||||
# --------------------- Building GCal event representation ----------------------------------------------------------
|
# --------------------- Building GCal event representation ------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def build_gcal_attendees_obj(event):
|
def build_gcal_attendees_obj(event):
|
||||||
"""Builds an attendees object that is inserted into the GCal event.
|
"""
|
||||||
Attendees are all users that have a google mail address."""
|
Builds an attendees object that is inserted into the GCal event.
|
||||||
|
|
||||||
|
Attendees are all users that have a Google mail address.
|
||||||
|
"""
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for userMapping in UserGCalCoupling.objects.all():
|
for user_mapping in UserGCalCoupling.objects.all():
|
||||||
u = userMapping.user
|
u = user_mapping.user
|
||||||
|
|
||||||
# No get or create here, since a create would trigger another synchronization
|
|
||||||
# participation = EventParticipation.get_or_create( u, event )
|
|
||||||
try:
|
try:
|
||||||
participation = EventParticipation.objects.get(event=event, user=u)
|
participation = EventParticipation.objects.get(event=event, user=u)
|
||||||
local_status = participation.status
|
local_status = participation.status
|
||||||
@@ -76,31 +118,33 @@ def build_gcal_attendees_obj(event):
|
|||||||
status = "needsAction"
|
status = "needsAction"
|
||||||
if local_status == "?":
|
if local_status == "?":
|
||||||
status = "tentative"
|
status = "tentative"
|
||||||
elif local_status == 'Yes':
|
elif local_status == "Yes":
|
||||||
status = "accepted"
|
status = "accepted"
|
||||||
elif local_status == 'No':
|
elif local_status == "No":
|
||||||
status = "declined"
|
status = "declined"
|
||||||
|
|
||||||
o = {
|
attendee = {
|
||||||
'id': userMapping.email,
|
"email": user_mapping.email,
|
||||||
'email': userMapping.email,
|
"displayName": u.username,
|
||||||
'displayName': u.username,
|
"comment": local_comment,
|
||||||
'comment': local_comment,
|
"responseStatus": status,
|
||||||
'responseStatus': status,
|
|
||||||
}
|
}
|
||||||
result.append(o)
|
result.append(attendee)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def build_gcal_event(event, timezone="Europe/Berlin"):
|
def build_gcal_event(event, timezone="Europe/Berlin"):
|
||||||
""" Builds a GCal event using a local event. """
|
"""Builds a GCal event using a local event."""
|
||||||
|
|
||||||
def create_date_time_obj(date, time_obj):
|
def create_datetime_obj(date, time_val):
|
||||||
if time_obj is None:
|
if time_val is None:
|
||||||
return {'date': text(date), 'timeZone': timezone}
|
return {"date": str(date), "timeZone": timezone}
|
||||||
else:
|
else:
|
||||||
return {'dateTime': text(date) + 'T' + text(time_obj), 'timeZone': timezone}
|
return {
|
||||||
|
"dateTime": f"{date}T{time_val}",
|
||||||
|
"timeZone": timezone,
|
||||||
|
}
|
||||||
|
|
||||||
start_date = event.date
|
start_date = event.date
|
||||||
end_date = event.end_date
|
end_date = event.end_date
|
||||||
@@ -116,196 +160,285 @@ def build_gcal_event(event, timezone="Europe/Berlin"):
|
|||||||
else:
|
else:
|
||||||
end_time = datetime.time(22, 30)
|
end_time = datetime.time(22, 30)
|
||||||
|
|
||||||
g_location = text(event.location)
|
g_location = str(event.location)
|
||||||
if event.map_location:
|
if event.map_location:
|
||||||
# Map location has the following format: latitude,longitude,zoomlevel
|
# Map location has the following format: latitude,longitude,zoomlevel
|
||||||
# the first two are needed
|
# the first two are needed
|
||||||
s = event.map_location.split(",")
|
parts = event.map_location.split(",")
|
||||||
g_location = text("%s,%s" % (s[0], s[1]))
|
if len(parts) >= 2:
|
||||||
|
g_location = f"{parts[0]},{parts[1]}"
|
||||||
|
|
||||||
|
gcal_settings = settings.GCAL_COUPLING
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'summary': text(settings.GCAL_COUPLING['eventPrefix'] + event.title),
|
"summary": gcal_settings["eventPrefix"] + event.title,
|
||||||
'description': text(event.desc),
|
"description": str(event.desc),
|
||||||
'location': g_location,
|
"location": g_location,
|
||||||
'start': create_date_time_obj(start_date, start_time),
|
"start": create_datetime_obj(start_date, start_time),
|
||||||
'end': create_date_time_obj(end_date, end_time),
|
"end": create_datetime_obj(end_date, end_time),
|
||||||
'extendedProperties': {
|
"extendedProperties": {
|
||||||
'private': {
|
"private": {
|
||||||
'blechreizEvent': 'true',
|
"blechreizEvent": "true",
|
||||||
'blechreizID': event.id,
|
"blechreizID": str(event.id),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'attendees': build_gcal_attendees_obj(event),
|
"attendees": build_gcal_attendees_obj(event),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------ Callback Functions -------------------------------------------------------------------
|
# ------------------------------ Callback Functions ------------------------------------------------
|
||||||
|
|
||||||
def on_gcal_event_created(_, response, exception=None):
|
|
||||||
"""Callback function for created events to enter new gcal id in the mapping table"""
|
def on_gcal_event_created(request_id, response, exception=None):
|
||||||
|
"""Callback function for created events to enter new gcal id in the mapping table."""
|
||||||
if exception is not None:
|
if exception is not None:
|
||||||
logger.error("on_gcal_event_created: Exception " + str(exception))
|
logger.error(f"Error creating GCal event: {exception}")
|
||||||
raise exception
|
raise exception
|
||||||
|
|
||||||
google_id = response['id']
|
google_id = response["id"]
|
||||||
django_id = response['extendedProperties']['private']['blechreizID']
|
django_id = response["extendedProperties"]["private"]["blechreizID"]
|
||||||
mapping = GCalMapping(gcal_id=google_id, event=Event.objects.get(pk=django_id))
|
|
||||||
|
try:
|
||||||
|
event = Event.objects.get(pk=django_id)
|
||||||
|
mapping = GCalMapping(gcal_id=google_id, event=event)
|
||||||
mapping.save()
|
mapping.save()
|
||||||
|
logger.info(f"Created mapping: GCal {google_id} <-> Event {django_id}")
|
||||||
|
except Event.DoesNotExist:
|
||||||
|
logger.error(f"Event {django_id} not found when creating GCal mapping")
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------ GCal Api Calls --------------------------------------------------------------------
|
# ------------------------------ GCal Api Calls -------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def get_all_gcal_events(service, from_now=False):
|
def get_all_gcal_events(service, from_now=False):
|
||||||
"""Retrieves all gcal events with custom property blechreizEvent=True i.e. all
|
"""
|
||||||
events that have been created by this script."""
|
Retrieves all gcal events with custom property blechreizEvent=True.
|
||||||
|
|
||||||
|
These are all events that have been created by this script.
|
||||||
|
"""
|
||||||
if from_now:
|
if from_now:
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
min_time = now.strftime("%Y-%m-%dT%H:%M:%S-00:00")
|
min_time = now.strftime("%Y-%m-%dT%H:%M:%S-00:00")
|
||||||
else:
|
else:
|
||||||
min_time = '2000-01-01T00:00:00-00:00'
|
min_time = "2000-01-01T00:00:00-00:00"
|
||||||
|
|
||||||
events = service.events().list(
|
try:
|
||||||
calendarId='primary',
|
events = (
|
||||||
|
service.events()
|
||||||
|
.list(
|
||||||
|
calendarId="primary",
|
||||||
singleEvents=True,
|
singleEvents=True,
|
||||||
maxResults=1000,
|
maxResults=1000,
|
||||||
orderBy='startTime',
|
orderBy="startTime",
|
||||||
timeMin=min_time,
|
timeMin=min_time,
|
||||||
timeMax='2100-01-01T00:00:00-00:00',
|
timeMax="2100-01-01T00:00:00-00:00",
|
||||||
privateExtendedProperty='blechreizEvent=true',
|
privateExtendedProperty="blechreizEvent=true",
|
||||||
).execute()
|
)
|
||||||
return events['items']
|
.execute()
|
||||||
|
)
|
||||||
|
return events.get("items", [])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to retrieve GCal events: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def create_gcal_event(service, event, timezone="Europe/Berlin"):
|
def create_gcal_event_request(service, event, timezone="Europe/Berlin"):
|
||||||
"""Creates a new gcal event using a local event"""
|
"""Creates a request to create a new gcal event using a local event."""
|
||||||
google_event = build_gcal_event(event, timezone)
|
google_event = build_gcal_event(event, timezone)
|
||||||
return service.events().insert(calendarId='primary', body=google_event)
|
return service.events().insert(calendarId="primary", body=google_event)
|
||||||
|
|
||||||
|
|
||||||
def update_gcal_event(service, event, timezone="Europe/Berlin"):
|
def update_gcal_event_request(service, event, timezone="Europe/Berlin"):
|
||||||
"""Updates an existing gcal event, using a local event"""
|
"""Creates a request to update an existing gcal event using a local event."""
|
||||||
google_event = build_gcal_event(event, timezone)
|
google_event = build_gcal_event(event, timezone)
|
||||||
try:
|
try:
|
||||||
mapping = GCalMapping.objects.get(event=event)
|
mapping = GCalMapping.objects.get(event=event)
|
||||||
except GCalMapping.DoesNotExist:
|
except GCalMapping.DoesNotExist:
|
||||||
return create_gcal_event(service, event, timezone)
|
return create_gcal_event_request(service, event, timezone)
|
||||||
|
|
||||||
return service.events().patch(calendarId='primary', eventId=mapping.gcal_id, body=google_event)
|
return service.events().patch(
|
||||||
|
calendarId="primary", eventId=mapping.gcal_id, body=google_event
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def delete_gcal_event(service, event):
|
def delete_gcal_event_request(service, event):
|
||||||
"""Deletes gcal that belongs to the given local event"""
|
"""Creates a request to delete gcal event that belongs to the given local event."""
|
||||||
|
try:
|
||||||
mapping = GCalMapping.objects.get(event=event)
|
mapping = GCalMapping.objects.get(event=event)
|
||||||
gcal_id = mapping.gcal_id
|
gcal_id = mapping.gcal_id
|
||||||
mapping.delete()
|
mapping.delete()
|
||||||
return service.events().delete(calendarId='primary', eventId=gcal_id)
|
return service.events().delete(calendarId="primary", eventId=gcal_id)
|
||||||
|
except GCalMapping.DoesNotExist:
|
||||||
|
logger.warning(f"No GCal mapping found for event {event.id}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------- Synchronization -------------------------------------------------------------
|
# ------------------------------------- Synchronization ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def delete_all_gcal_events(service=None):
|
def delete_all_gcal_events(service=None):
|
||||||
"""Deletes all gcal events that have been created by this script"""
|
"""Deletes all gcal events that have been created by this script."""
|
||||||
|
|
||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
gcal_ids = [ev['id'] for ev in get_all_gcal_events(service)]
|
if service is None:
|
||||||
num_ids = len(gcal_ids)
|
logger.error("No service object available")
|
||||||
if num_ids == 0:
|
return 0
|
||||||
return num_ids
|
|
||||||
|
gcal_events = get_all_gcal_events(service)
|
||||||
|
gcal_ids = [ev["id"] for ev in gcal_events]
|
||||||
|
count = len(gcal_ids)
|
||||||
|
|
||||||
|
if count == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Use batch request for efficiency
|
||||||
|
from googleapiclient.http import BatchHttpRequest
|
||||||
|
|
||||||
batch = BatchHttpRequest()
|
batch = BatchHttpRequest()
|
||||||
for ev_id in gcal_ids:
|
for gcal_id in gcal_ids:
|
||||||
batch.add(service.events().delete(calendarId='primary', eventId=ev_id))
|
batch.add(service.events().delete(calendarId="primary", eventId=gcal_id))
|
||||||
|
|
||||||
|
try:
|
||||||
batch.execute()
|
batch.execute()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error deleting GCal events: {e}")
|
||||||
|
|
||||||
GCalMapping.objects.all().delete()
|
GCalMapping.objects.all().delete()
|
||||||
|
|
||||||
return num_ids
|
return count
|
||||||
|
|
||||||
|
|
||||||
def sync_from_local_to_google(service=None):
|
def sync_from_local_to_google(service=None):
|
||||||
""" Creates a google event for each local event (if it does not exist yet) and deletes all google events
|
|
||||||
that are not found in local database. Updates participation info of gcal events using local data
|
|
||||||
"""
|
"""
|
||||||
|
Creates a google event for each local event (if it does not exist yet) and
|
||||||
|
deletes all google events that are not found in local database.
|
||||||
|
Updates participation info of gcal events using local data.
|
||||||
|
"""
|
||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.error("No service object available for sync")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
all_events = get_all_gcal_events(service)
|
all_events = get_all_gcal_events(service)
|
||||||
|
|
||||||
events_at_google_django_id = set()
|
events_at_google_django_id = set()
|
||||||
events_at_google_google_id = set()
|
events_at_google_google_id = set()
|
||||||
for gcal_ev in all_events:
|
|
||||||
events_at_google_django_id.add(int(gcal_ev['extendedProperties']['private']['blechreizID']))
|
|
||||||
events_at_google_google_id.add(gcal_ev['id'])
|
|
||||||
|
|
||||||
local_events_django_id = set(Event.objects.all().values_list('pk', flat=True))
|
for gcal_ev in all_events:
|
||||||
local_events_google_id = set(GCalMapping.objects.all().values_list('gcal_id', flat=True))
|
try:
|
||||||
|
django_id = int(gcal_ev["extendedProperties"]["private"]["blechreizID"])
|
||||||
|
events_at_google_django_id.add(django_id)
|
||||||
|
events_at_google_google_id.add(gcal_ev["id"])
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
logger.warning(f"Invalid GCal event structure: {e}")
|
||||||
|
|
||||||
|
local_events_django_id = set(Event.objects.all().values_list("pk", flat=True))
|
||||||
|
local_events_google_id = set(
|
||||||
|
GCalMapping.objects.all().values_list("gcal_id", flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
events_to_create_django_id = local_events_django_id - events_at_google_django_id
|
events_to_create_django_id = local_events_django_id - events_at_google_django_id
|
||||||
events_to_delete_google_id = events_at_google_google_id - local_events_google_id
|
events_to_delete_google_id = events_at_google_google_id - local_events_google_id
|
||||||
|
|
||||||
|
from googleapiclient.http import BatchHttpRequest
|
||||||
|
|
||||||
batch = BatchHttpRequest()
|
batch = BatchHttpRequest()
|
||||||
|
|
||||||
batch_is_empty = True
|
batch_is_empty = True
|
||||||
|
|
||||||
for event_django_id in events_to_create_django_id:
|
for event_django_id in events_to_create_django_id:
|
||||||
batch.add(create_gcal_event(service, Event.objects.get(pk=event_django_id)), callback=on_gcal_event_created)
|
|
||||||
batch_is_empty = False
|
|
||||||
|
|
||||||
for eventGoogleID in events_to_delete_google_id:
|
|
||||||
batch.add(service.events().delete(calendarId='primary', eventId=eventGoogleID))
|
|
||||||
batch_is_empty = False
|
|
||||||
|
|
||||||
for gcal_ev in all_events:
|
|
||||||
event_django_id = int(gcal_ev['extendedProperties']['private']['blechreizID'])
|
|
||||||
try:
|
try:
|
||||||
django_ev = Event.objects.get(pk=event_django_id)
|
event = Event.objects.get(pk=event_django_id)
|
||||||
if 'attendees' not in gcal_ev:
|
batch.add(
|
||||||
gcal_ev['attendees'] = []
|
create_gcal_event_request(service, event),
|
||||||
|
callback=on_gcal_event_created,
|
||||||
if gcal_ev['attendees'] != build_gcal_attendees_obj(django_ev):
|
)
|
||||||
batch.add(update_gcal_event(service, django_ev))
|
|
||||||
batch_is_empty = False
|
batch_is_empty = False
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
for event_google_id in events_to_delete_google_id:
|
||||||
|
batch.add(
|
||||||
|
service.events().delete(calendarId="primary", eventId=event_google_id)
|
||||||
|
)
|
||||||
|
batch_is_empty = False
|
||||||
|
|
||||||
|
for gcal_ev in all_events:
|
||||||
|
try:
|
||||||
|
event_django_id = int(
|
||||||
|
gcal_ev["extendedProperties"]["private"]["blechreizID"]
|
||||||
|
)
|
||||||
|
django_ev = Event.objects.get(pk=event_django_id)
|
||||||
|
|
||||||
|
gcal_attendees = gcal_ev.get("attendees", [])
|
||||||
|
local_attendees = build_gcal_attendees_obj(django_ev)
|
||||||
|
|
||||||
|
# Simple comparison - check if attendees differ
|
||||||
|
if gcal_attendees != local_attendees:
|
||||||
|
batch.add(update_gcal_event_request(service, django_ev))
|
||||||
|
batch_is_empty = False
|
||||||
|
except Event.DoesNotExist:
|
||||||
|
pass
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
if not batch_is_empty:
|
if not batch_is_empty:
|
||||||
|
try:
|
||||||
batch.execute()
|
batch.execute()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error executing batch request: {e}")
|
||||||
|
|
||||||
return len(events_to_create_django_id), len(events_to_delete_google_id)
|
return len(events_to_create_django_id), len(events_to_delete_google_id)
|
||||||
|
|
||||||
|
|
||||||
def sync_from_google_to_local(service=None):
|
def sync_from_google_to_local(service=None):
|
||||||
"""Retrieves only participation infos for all events and updates local database if anything has changed. """
|
"""
|
||||||
|
Retrieves only participation infos for all events and
|
||||||
|
updates local database if anything has changed.
|
||||||
|
"""
|
||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.error("No service object available for sync")
|
||||||
|
return False
|
||||||
|
|
||||||
new_status_received = False
|
new_status_received = False
|
||||||
all_events = get_all_gcal_events(service, from_now=True)
|
all_events = get_all_gcal_events(service, from_now=True)
|
||||||
|
|
||||||
for e in all_events:
|
for e in all_events:
|
||||||
local_id = e['extendedProperties']['private']['blechreizID']
|
try:
|
||||||
|
local_id = e["extendedProperties"]["private"]["blechreizID"]
|
||||||
local_event = Event.objects.get(pk=local_id)
|
local_event = Event.objects.get(pk=local_id)
|
||||||
for a in e['attendees']:
|
|
||||||
user = UserGCalCoupling.objects.get(email=a['email']).user
|
for a in e.get("attendees", []):
|
||||||
|
try:
|
||||||
|
user_coupling = UserGCalCoupling.objects.get(email=a["email"])
|
||||||
|
user = user_coupling.user
|
||||||
part = EventParticipation.get_or_create(user, local_event)
|
part = EventParticipation.get_or_create(user, local_event)
|
||||||
if 'comment' in a:
|
|
||||||
part.comment = a['comment']
|
|
||||||
|
|
||||||
if a['responseStatus'] == 'needsAction':
|
if "comment" in a:
|
||||||
|
part.comment = a["comment"]
|
||||||
|
|
||||||
|
response_status = a.get("responseStatus", "needsAction")
|
||||||
|
if response_status == "needsAction":
|
||||||
part.status = "-"
|
part.status = "-"
|
||||||
elif a['responseStatus'] == 'tentative':
|
elif response_status == "tentative":
|
||||||
part.status = '?'
|
part.status = "?"
|
||||||
elif a['responseStatus'] == 'accepted':
|
elif response_status == "accepted":
|
||||||
part.status = 'Yes'
|
part.status = "Yes"
|
||||||
elif a['responseStatus'] == 'declined':
|
elif response_status == "declined":
|
||||||
part.status = 'No'
|
part.status = "No"
|
||||||
else:
|
else:
|
||||||
logger.error("Unknown response status when mapping gcal event: " + a['responseStatus'])
|
logger.error(
|
||||||
|
f"Unknown response status when mapping gcal event: {response_status}"
|
||||||
|
)
|
||||||
|
|
||||||
prev = EventParticipation.objects.get(event=part.event, user=part.user)
|
prev = EventParticipation.objects.get(
|
||||||
|
event=part.event, user=part.user
|
||||||
|
)
|
||||||
|
|
||||||
# Important: Save only if the participation info has changed
|
# Important: Save only if the participation info has changed
|
||||||
# otherwise everything is synced back to google via the post save signal
|
# otherwise everything is synced back to google via the post save signal
|
||||||
@@ -314,15 +447,28 @@ def sync_from_google_to_local(service=None):
|
|||||||
part.save()
|
part.save()
|
||||||
new_status_received = True
|
new_status_received = True
|
||||||
|
|
||||||
|
except UserGCalCoupling.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Event.DoesNotExist:
|
||||||
|
logger.warning(f"Event with id {local_id} not found in local database")
|
||||||
|
except KeyError as e:
|
||||||
|
logger.warning(f"Invalid event structure: {e}")
|
||||||
|
|
||||||
return new_status_received
|
return new_status_received
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------- Synchronization ----------------------------------------------------
|
# ------------------------------------- Push Channel Management ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def check_gcal_subscription(service=None, time_to_live=14 * 24 * 3600, renew_before_expiry=None):
|
def check_gcal_subscription(
|
||||||
"""Google offers a push service if any event information has changed. This works using a so called
|
service=None, time_to_live=14 * 24 * 3600, renew_before_expiry=None
|
||||||
channel, which has a certain time to live. This method checks that a valid channel exists:
|
):
|
||||||
|
"""
|
||||||
|
Google offers a push service if any event information has changed.
|
||||||
|
|
||||||
|
This works using a so called channel, which has a certain time to live.
|
||||||
|
This method checks that a valid channel exists:
|
||||||
- if none exists a new one is created
|
- if none exists a new one is created
|
||||||
- if existing channel does expire soon, the channel is renewed
|
- if existing channel does expire soon, the channel is renewed
|
||||||
- if channel has already expired a sync is triggered and a new channel is created
|
- if channel has already expired a sync is triggered and a new channel is created
|
||||||
@@ -330,37 +476,40 @@ def check_gcal_subscription(service=None, time_to_live=14 * 24 * 3600, renew_bef
|
|||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.error("No service object available")
|
||||||
|
return
|
||||||
|
|
||||||
if renew_before_expiry is None:
|
if renew_before_expiry is None:
|
||||||
renew_before_expiry = 0.8 * time_to_live
|
renew_before_expiry = 0.8 * time_to_live
|
||||||
|
|
||||||
callback_url = settings.GCAL_COUPLING['push_url']
|
callback_url = settings.GCAL_COUPLING["push_url"]
|
||||||
|
|
||||||
# Test if a channel already exists for this callbackURL
|
|
||||||
try:
|
try:
|
||||||
db_channel = GCalPushChannel.objects.get(address=callback_url)
|
db_channel = GCalPushChannel.objects.get(address=callback_url)
|
||||||
g_channel = db_channel.to_google_channel()
|
|
||||||
|
|
||||||
# if expiration time between 0 and two days: stop and create new channel
|
|
||||||
cur_time = int(time.time() * 1000)
|
cur_time = int(time.time() * 1000)
|
||||||
if g_channel.expiration > cur_time:
|
|
||||||
|
if db_channel.expiration > cur_time:
|
||||||
# not yet expired
|
# not yet expired
|
||||||
if cur_time + renew_before_expiry * 1000 > g_channel.expiration:
|
if cur_time + renew_before_expiry * 1000 > db_channel.expiration:
|
||||||
# will expire in less than "renewBeforeExpiry"
|
# will expire in less than "renew_before_expiry"
|
||||||
logger.info("Renewing Google Calendar Subscription: " + callback_url)
|
logger.info(f"Renewing Google Calendar Subscription: {callback_url}")
|
||||||
GCalPushChannel.stop(service, g_channel)
|
db_channel.stop(service)
|
||||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||||
else:
|
else:
|
||||||
logger.info("Channel active until %d " % (g_channel.expiration,))
|
logger.info(f"Channel active until {db_channel.expiration}")
|
||||||
else:
|
else:
|
||||||
logger.info("Google calendar subscription had expired - getting new subscription")
|
logger.info(
|
||||||
|
"Google calendar subscription had expired - getting new subscription"
|
||||||
|
)
|
||||||
# to get back in sync again we have to decide which data to take
|
# to get back in sync again we have to decide which data to take
|
||||||
# so we use the local data as reference
|
# so we use the local data as reference
|
||||||
sync_from_local_to_google(service)
|
sync_from_local_to_google(service)
|
||||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||||
|
|
||||||
except GCalPushChannel.DoesNotExist:
|
except GCalPushChannel.DoesNotExist:
|
||||||
# create new channel and save it in database
|
logger.info(f"No GCalCallback Channel exists yet for: {callback_url}")
|
||||||
logger.info("No CGalCallback Channel exists yet for: " + callback_url)
|
|
||||||
# to get back in sync again we have to decide which data to take
|
# to get back in sync again we have to decide which data to take
|
||||||
# so we use the local data as reference
|
# so we use the local data as reference
|
||||||
sync_from_local_to_google(service)
|
sync_from_local_to_google(service)
|
||||||
@@ -368,45 +517,68 @@ def check_gcal_subscription(service=None, time_to_live=14 * 24 * 3600, renew_bef
|
|||||||
|
|
||||||
|
|
||||||
def stop_all_gcal_subscriptions(service=None):
|
def stop_all_gcal_subscriptions(service=None):
|
||||||
"""Stops the channel subscription """
|
"""Stops all channel subscriptions."""
|
||||||
|
|
||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
for dbChannel in GCalPushChannel.objects.all():
|
if service is None:
|
||||||
logger.info("Stopping %s expiry at %d " % (dbChannel.id, dbChannel.expiration))
|
logger.error("No service object available")
|
||||||
GCalPushChannel.stop(service, dbChannel.to_google_channel())
|
return
|
||||||
|
|
||||||
|
for db_channel in GCalPushChannel.objects.all():
|
||||||
|
logger.info(
|
||||||
|
f"Stopping channel {db_channel.id} expiry at {db_channel.expiration}"
|
||||||
|
)
|
||||||
|
db_channel.stop(service)
|
||||||
|
|
||||||
|
|
||||||
def check_if_google_callback_is_valid(token, channel_id, resource_id, service=None):
|
def check_if_google_callback_is_valid(token, channel_id, resource_id, service=None):
|
||||||
|
"""Validate an incoming Google Calendar push notification."""
|
||||||
if service is None:
|
if service is None:
|
||||||
service = get_service_object()
|
service = get_service_object()
|
||||||
|
|
||||||
all_channels = GCalPushChannel.objects.all()
|
all_channels = GCalPushChannel.objects.all()
|
||||||
|
|
||||||
if len(all_channels) == 0:
|
if len(all_channels) == 0:
|
||||||
return False # no known subscriptions -> callback has to be from an old channel
|
return False # no known subscriptions -> callback has to be from an old channel
|
||||||
|
|
||||||
if len(all_channels) > 1:
|
if len(all_channels) > 1:
|
||||||
logger.warning("Multiple GCal subscriptions! This is strange and probably an error. "
|
logger.warning(
|
||||||
"All channels are closed and one new is created. ")
|
"Multiple GCal subscriptions! This is strange and probably an error. "
|
||||||
|
"All channels are closed and one new is created."
|
||||||
|
)
|
||||||
stop_all_gcal_subscriptions(service)
|
stop_all_gcal_subscriptions(service)
|
||||||
check_gcal_subscription()
|
check_gcal_subscription()
|
||||||
all_channels = GCalPushChannel.objects.all()
|
all_channels = GCalPushChannel.objects.all()
|
||||||
|
|
||||||
assert (len(all_channels) == 1)
|
if len(all_channels) != 1:
|
||||||
|
return False
|
||||||
|
|
||||||
the_channel = all_channels[0]
|
the_channel = all_channels[0]
|
||||||
|
|
||||||
if channel_id != the_channel.id or resource_id != the_channel.resource_id or token != the_channel.token:
|
if (
|
||||||
logger.warning("Got GCal Response from an unexpected Channel"
|
channel_id != the_channel.id
|
||||||
"Got (%s,%s,%s) "
|
or resource_id != the_channel.resource_id
|
||||||
"expected (%s,%s,%s) "
|
or token != the_channel.token
|
||||||
"Old Channel is stopped."
|
):
|
||||||
% (channel_id, resource_id, token, the_channel.id, the_channel.resource_id, the_channel.token))
|
logger.warning(
|
||||||
|
f"Got GCal Response from an unexpected Channel. "
|
||||||
channel_to_stop = GCalPushChannel(id=channel_id, resource_id=resource_id, token=token)
|
f"Got ({channel_id}, {resource_id}, {token}) "
|
||||||
GCalPushChannel.stop(service, channel_to_stop.to_google_channel())
|
f"expected ({the_channel.id}, {the_channel.resource_id}, {the_channel.token}). "
|
||||||
|
f"Old Channel is stopped."
|
||||||
|
)
|
||||||
|
|
||||||
|
GCalPushChannel.stop_channel(service, channel_id, resource_id)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards compatibility aliases
|
||||||
|
syncFromLocalToGoogle = sync_from_local_to_google
|
||||||
|
syncFromGoogleToLocal = sync_from_google_to_local
|
||||||
|
checkIfGoogleCallbackIsValid = check_if_google_callback_is_valid
|
||||||
|
checkGCalSubscription = check_gcal_subscription
|
||||||
|
stopAllGCalSubscriptions = stop_all_gcal_subscriptions
|
||||||
|
deleteAllGCalEvents = delete_all_gcal_events
|
||||||
|
getServiceObject = get_service_object
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
from eventplanner_gcal.google_sync import check_gcal_subscription
|
from eventplanner_gcal.google_sync import checkGCalSubscription
|
||||||
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Checks if the GCal notification channel is still active'
|
help = 'Checks if the GCal notification channel is still active'
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle_noargs(self, **options):
|
||||||
print("Checking Subscription")
|
print ( "Checking Subscription")
|
||||||
check_gcal_subscription()
|
checkGCalSubscription()
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
from eventplanner_gcal.google_sync import delete_all_gcal_events
|
from eventplanner_gcal.google_sync import deleteAllGCalEvents
|
||||||
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Delete all events in the google calendar created by this app'
|
help = 'Delete all events in the google calendar created by this app'
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle_noargs(self, **options):
|
||||||
print("Deleting all GCal Events.")
|
print ("Deleting all GCal Events.")
|
||||||
nr_of_deleted_events = delete_all_gcal_events()
|
nrOfDeletedEvents = deleteAllGCalEvents()
|
||||||
print("Deleted %d events. To Restore them from local database run gcal_sync" % (nr_of_deleted_events,))
|
print ("Deleted %d events. To Restore them from local database run gcal_sync" % (nrOfDeletedEvents, ) )
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
from eventplanner_gcal.google_sync import stop_all_gcal_subscriptions
|
from eventplanner_gcal.google_sync import stopAllGCalSubscriptions
|
||||||
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Stops all GCal subscriptions'
|
help = 'Stops all GCal subscriptions'
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle_noargs(self, **options):
|
||||||
stop_all_gcal_subscriptions()
|
stopAllGCalSubscriptions()
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
from eventplanner_gcal.google_sync import sync_from_local_to_google
|
|
||||||
|
|
||||||
|
from eventplanner_gcal.google_sync import syncFromLocalToGoogle
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(NoArgsCommand):
|
||||||
help = 'Synchronize Google Calendar with locally stored Events'
|
help = 'Synchronize Google Calendar with locally stored Events'
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle_noargs(self, **options):
|
||||||
print("Running Sync")
|
print ( "Running Sync")
|
||||||
created, deleted = sync_from_local_to_google()
|
created, deleted = syncFromLocalToGoogle()
|
||||||
print("Created %d and deleted %d events" % (created, deleted))
|
print ( "Created %d and deleted %d events" % (created,deleted) )
|
||||||
|
|||||||
55
eventplanner_gcal/migrations/0001_initial.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Generated by Django 5.1.15 on 2026-03-30 19:15
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('eventplanner', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GCalMapping',
|
||||||
|
fields=[
|
||||||
|
('gcal_id', models.CharField(max_length=64)),
|
||||||
|
('event', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='eventplanner.event')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Google Calendar Mapping',
|
||||||
|
'verbose_name_plural': 'Google Calendar Mappings',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GCalPushChannel',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
|
||||||
|
('address', models.CharField(max_length=256)),
|
||||||
|
('token', models.CharField(max_length=128)),
|
||||||
|
('resource_id', models.CharField(max_length=128)),
|
||||||
|
('expiration', models.BigIntegerField()),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Google Calendar Push Channel',
|
||||||
|
'verbose_name_plural': 'Google Calendar Push Channels',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserGCalCoupling',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('email', models.CharField(max_length=1024)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User Google Calendar Coupling',
|
||||||
|
'verbose_name_plural': 'User Google Calendar Couplings',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
eventplanner_gcal/migrations/__init__.py
Normal file
@@ -1,59 +1,127 @@
|
|||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from eventplanner.models import Event
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from apiclient.channel import Channel
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from eventplanner.models import Event
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class UserGCalCoupling(models.Model):
|
class UserGCalCoupling(models.Model):
|
||||||
# For every user in this table the gcal coupling is activated
|
"""For every user in this table the gcal coupling is activated."""
|
||||||
|
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
email = models.CharField(max_length=1024)
|
email = models.CharField(max_length=1024)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "User Google Calendar Coupling"
|
||||||
|
verbose_name_plural = "User Google Calendar Couplings"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user.username} <-> {self.email}"
|
||||||
|
|
||||||
|
|
||||||
class GCalMapping(models.Model):
|
class GCalMapping(models.Model):
|
||||||
"""Mapping between event id at google and local event id"""
|
"""Mapping between event id at google and local event id."""
|
||||||
|
|
||||||
gcal_id = models.CharField(max_length=64)
|
gcal_id = models.CharField(max_length=64)
|
||||||
event = models.OneToOneField(Event, primary_key=True, on_delete=models.CASCADE)
|
event = models.OneToOneField(Event, on_delete=models.CASCADE, primary_key=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Google Calendar Mapping"
|
||||||
|
verbose_name_plural = "Google Calendar Mappings"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"GCal:{self.gcal_id} <-> Event:{self.event_id}"
|
||||||
|
|
||||||
|
|
||||||
class GCalPushChannel(models.Model):
|
class GCalPushChannel(models.Model):
|
||||||
"""This table has either zero or one entry. Required to store if a channel already exists,
|
"""This table has either zero or one entry. Required to store if a channel already exists,
|
||||||
when it expires and how to stop (renew) the channel
|
when it expires and how to stop (renew) the channel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = models.CharField(max_length=128, primary_key=True)
|
id = models.CharField(max_length=128, primary_key=True)
|
||||||
address = models.CharField(max_length=256)
|
address = models.CharField(max_length=256)
|
||||||
token = models.CharField(max_length=128)
|
token = models.CharField(max_length=128)
|
||||||
resource_id = models.CharField(max_length=128)
|
resource_id = models.CharField(max_length=128)
|
||||||
expiration = models.IntegerField()
|
expiration = models.BigIntegerField()
|
||||||
|
|
||||||
def to_google_channel(self):
|
class Meta:
|
||||||
return Channel('web_hook', self.id, self.token, self.address, self.expiration, resource_id=self.resource_id)
|
verbose_name = "Google Calendar Push Channel"
|
||||||
|
verbose_name_plural = "Google Calendar Push Channels"
|
||||||
|
|
||||||
@staticmethod
|
def __str__(self):
|
||||||
def from_google_channel(google_channel):
|
return f"Channel {self.id} expires at {self.expiration}"
|
||||||
return GCalPushChannel(id=google_channel.id,
|
|
||||||
address=google_channel.address,
|
|
||||||
token=google_channel.token,
|
|
||||||
expiration=google_channel.expiration,
|
|
||||||
resource_id=google_channel.resource_id)
|
|
||||||
|
|
||||||
@staticmethod
|
def to_channel_dict(self):
|
||||||
def create_new(callback_url, service, ttl=None):
|
"""Return a dictionary representation for the Google API."""
|
||||||
gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callback_url, params={'ttl': int(ttl)})
|
return {
|
||||||
response = service.events().watch(calendarId='primary', body=gChannel.body()).execute()
|
"id": self.id,
|
||||||
gChannel.update(response)
|
"type": "web_hook",
|
||||||
|
"address": self.address,
|
||||||
|
"token": self.token,
|
||||||
|
"expiration": self.expiration,
|
||||||
|
"resourceId": self.resource_id,
|
||||||
|
}
|
||||||
|
|
||||||
dbChannel = GCalPushChannel.from_google_channel(gChannel)
|
@classmethod
|
||||||
dbChannel.save()
|
def from_response(cls, response, address, token):
|
||||||
|
"""Create a GCalPushChannel from a Google API response."""
|
||||||
|
return cls(
|
||||||
|
id=response["id"],
|
||||||
|
address=address,
|
||||||
|
token=token,
|
||||||
|
expiration=int(response.get("expiration", 0)),
|
||||||
|
resource_id=response.get("resourceId", ""),
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def stop(service, google_channel):
|
def create_new(cls, callback_url, service, ttl=None):
|
||||||
channel_service = service.channels()
|
"""Create a new push channel subscription."""
|
||||||
channel_service.stop(body=google_channel.body()).execute()
|
channel_id = str(uuid.uuid4())
|
||||||
GCalPushChannel.from_google_channel(google_channel).delete()
|
token = "blechreizGcal"
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"id": channel_id,
|
||||||
|
"type": "web_hook",
|
||||||
|
"address": callback_url,
|
||||||
|
"token": token,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttl:
|
||||||
|
body["params"] = {"ttl": str(int(ttl))}
|
||||||
|
|
||||||
|
response = service.events().watch(calendarId="primary", body=body).execute()
|
||||||
|
|
||||||
|
db_channel = cls.from_response(response, callback_url, token)
|
||||||
|
db_channel.save()
|
||||||
|
return db_channel
|
||||||
|
|
||||||
|
def stop(self, service):
|
||||||
|
"""Stop this push channel subscription."""
|
||||||
|
body = {
|
||||||
|
"id": self.id,
|
||||||
|
"resourceId": self.resource_id,
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
service.channels().stop(body=body).execute()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to stop channel {self.id}: {e}")
|
||||||
|
self.delete()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stop_channel(cls, service, channel_id, resource_id):
|
||||||
|
"""Stop a push channel by ID and resource ID."""
|
||||||
|
body = {
|
||||||
|
"id": channel_id,
|
||||||
|
"resourceId": resource_id,
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
service.channels().stop(body=body).execute()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to stop channel {channel_id}: {e}")
|
||||||
|
|
||||||
|
# Also delete from database if it exists
|
||||||
|
cls.objects.filter(id=channel_id).delete()
|
||||||
|
|||||||
@@ -1,61 +1,120 @@
|
|||||||
from django.db.models.signals import post_save, pre_delete
|
"""
|
||||||
from django.dispatch import receiver
|
Django signal handlers for Google Calendar synchronization.
|
||||||
from eventplanner.models import Event, EventParticipation
|
|
||||||
from eventplanner_gcal.google_sync import get_service_object, \
|
These handlers are currently disabled (early return) but can be enabled
|
||||||
create_gcal_event, delete_gcal_event, update_gcal_event, on_gcal_event_created
|
to automatically sync events with Google Calendar when they are created,
|
||||||
|
updated, or deleted.
|
||||||
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from django.conf import settings
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models.signals import post_save, pre_delete
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from eventplanner.models import Event, EventParticipation
|
||||||
|
|
||||||
|
from .google_sync import (
|
||||||
|
create_gcal_event_request,
|
||||||
|
delete_gcal_event_request,
|
||||||
|
get_service_object,
|
||||||
|
on_gcal_event_created,
|
||||||
|
update_gcal_event_request,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# @receiver( post_save, sender=User )
|
# @receiver(post_save, sender=User)
|
||||||
# def user_changed( **kwargs ):
|
# def user_changed(**kwargs):
|
||||||
|
# """Sync with Google when user information changes."""
|
||||||
# logger.info("Synchronizing with google - user information changed")
|
# logger.info("Synchronizing with google - user information changed")
|
||||||
# syncFromLocalToGoogle( getServiceObject() )
|
# sync_from_local_to_google(get_service_object())
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Event)
|
@receiver(post_save, sender=Event)
|
||||||
def event_post_save_handler(**kwargs):
|
def event_post_save_handler(sender, instance, created, **kwargs):
|
||||||
if not settings.GCAL_SYNC_ENABLED:
|
"""
|
||||||
|
Handle Event post_save signal.
|
||||||
|
|
||||||
|
Creates or updates the corresponding Google Calendar event.
|
||||||
|
Currently disabled - remove the early return to enable.
|
||||||
|
"""
|
||||||
|
# Disabled - remove this return statement to enable auto-sync
|
||||||
|
return
|
||||||
|
|
||||||
|
event = instance
|
||||||
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.warning("No Google Calendar service available")
|
||||||
return
|
return
|
||||||
|
|
||||||
event = kwargs['instance']
|
|
||||||
created = kwargs['created']
|
|
||||||
try:
|
try:
|
||||||
if created:
|
if created:
|
||||||
logger.info("Creating Gcal event")
|
logger.info(f"Creating GCal event for Event {event.id}")
|
||||||
response = create_gcal_event(get_service_object(), event).execute()
|
request = create_gcal_event_request(service, event)
|
||||||
|
response = request.execute()
|
||||||
on_gcal_event_created(None, response, None)
|
on_gcal_event_created(None, response, None)
|
||||||
else:
|
else:
|
||||||
logger.info("Updating Gcal event")
|
logger.info(f"Updating GCal event for Event {event.id}")
|
||||||
update_gcal_event(get_service_object(), event).execute()
|
request = update_gcal_event_request(service, event)
|
||||||
|
request.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error updating Gcal event" + str(e))
|
logger.error(f"Error syncing event {event.id} to GCal: {e}")
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=Event)
|
@receiver(pre_delete, sender=Event)
|
||||||
def event_pre_delete_handler(**kwargs):
|
def event_pre_delete_handler(sender, instance, **kwargs):
|
||||||
if not settings.GCAL_SYNC_ENABLED:
|
"""
|
||||||
|
Handle Event pre_delete signal.
|
||||||
|
|
||||||
|
Deletes the corresponding Google Calendar event.
|
||||||
|
Currently disabled - remove the early return to enable.
|
||||||
|
"""
|
||||||
|
# Disabled - remove this return statement to enable auto-sync
|
||||||
|
return
|
||||||
|
|
||||||
|
event = instance
|
||||||
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.warning("No Google Calendar service available")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
event = kwargs['instance']
|
logger.info(f"Deleting GCal event for Event {event.id}")
|
||||||
logger.info("Deleting Gcal event")
|
request = delete_gcal_event_request(service, event)
|
||||||
delete_gcal_event(get_service_object(), event).execute()
|
if request:
|
||||||
|
request.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error deleting GCAL event" + str(e))
|
logger.error(f"Error deleting GCal event for Event {event.id}: {e}")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=EventParticipation)
|
@receiver(post_save, sender=EventParticipation)
|
||||||
def participation_post_save_handler(**kwargs):
|
def participation_post_save_handler(sender, instance, **kwargs):
|
||||||
if not settings.GCAL_SYNC_ENABLED:
|
"""
|
||||||
|
Handle EventParticipation post_save signal.
|
||||||
|
|
||||||
|
Updates the Google Calendar event when participation changes.
|
||||||
|
Currently disabled - remove the early return to enable.
|
||||||
|
"""
|
||||||
|
# Disabled - remove this return statement to enable auto-sync
|
||||||
|
return
|
||||||
|
|
||||||
|
participation = instance
|
||||||
|
service = get_service_object()
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
logger.warning("No Google Calendar service available")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
participation = kwargs['instance']
|
logger.info(
|
||||||
logger.info("Participation post save -> update gcal")
|
f"Participation changed for Event {participation.event.id} "
|
||||||
update_gcal_event(get_service_object(), participation.event).execute()
|
f"by User {participation.user.username}"
|
||||||
|
)
|
||||||
|
request = update_gcal_event_request(service, participation.event)
|
||||||
|
request.execute()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error deleting GCAL event" + str(e))
|
logger.error(f"Error updating GCal event for participation change: {e}")
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "website/base.html" %}
|
{% extends "website/base.html" %}
|
||||||
{% load sekizai_tags staticfiles %}
|
{% load sekizai_tags static %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% addtoblock "css" strip %}
|
{% addtoblock "css" strip %}
|
||||||
<link rel="stylesheet" href="{{STATIC_URL}}css/bootstrap-switch.min.css" type="text/css" media="screen" />
|
<link rel="stylesheet" href="{{STATIC_URL}}/css/bootstrap-switch.min.css" type="text/css" media="screen" />
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
{% addtoblock "css" %}
|
{% addtoblock "css" %}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
{% addtoblock "js" strip %}
|
{% addtoblock "js" strip %}
|
||||||
<script src="{{STATIC_URL}}js/bootstrap-switch.min.js"></script>
|
<script src="{{STATIC_URL}}/js/bootstrap-switch.min.js"></script>
|
||||||
{% endaddtoblock %}
|
{% endaddtoblock %}
|
||||||
|
|
||||||
{% addtoblock "js" %}
|
{% addtoblock "js" %}
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
weil man alle anderen eigenen Termine auch im Blick hat.
|
weil man alle anderen eigenen Termine auch im Blick hat.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<img src="{{STATIC_URL}}img/screenshot.png">
|
<img src="{{STATIC_URL}}/img/screenshot.png">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<h5>SO GEHTS:</h5>
|
<h5>SO GEHTS:</h5>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="span3 offset1">
|
<div class="span3 offset1">
|
||||||
<img src="{{STATIC_URL}}img/google_cal.png">
|
<img src="{{STATIC_URL}}/img/google_cal.png">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from .views import run_sync, gcal_api_callback, manage
|
from . import views
|
||||||
|
|
||||||
|
app_name = "eventplanner_gcal"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^runSync$', run_sync),
|
path("runSync/", views.run_sync, name="run_sync"),
|
||||||
url(r'^gcalApiCallback$', gcal_api_callback),
|
path("gcalApiCallback/", views.gcal_api_callback, name="gcal_api_callback"),
|
||||||
url(r'^manage$', manage),
|
path("manage/", views.manage, name="manage"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,63 +1,101 @@
|
|||||||
from django.shortcuts import redirect
|
"""
|
||||||
from eventplanner_gcal.google_sync import sync_from_google_to_local, sync_from_local_to_google
|
Views for Google Calendar integration management.
|
||||||
from django.http import HttpResponse
|
"""
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
|
||||||
from eventplanner_gcal.google_sync import check_if_google_callback_is_valid
|
|
||||||
from eventplanner_gcal.models import UserGCalCoupling
|
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
from .google_sync import (
|
||||||
|
check_if_google_callback_is_valid,
|
||||||
|
sync_from_google_to_local,
|
||||||
|
sync_from_local_to_google,
|
||||||
|
)
|
||||||
|
from .models import UserGCalCoupling
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def run_sync(request):
|
def run_sync(request):
|
||||||
|
"""Manually trigger a sync from local to Google Calendar."""
|
||||||
sync_from_local_to_google()
|
sync_from_local_to_google()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
def manage(request):
|
def manage(request):
|
||||||
if request.method == 'POST':
|
"""
|
||||||
if request.POST['activate'] == "1":
|
View for managing Google Calendar integration settings.
|
||||||
|
|
||||||
|
Allows users to enable/disable GCal sync and configure their email.
|
||||||
|
"""
|
||||||
|
if request.method == "POST":
|
||||||
|
activate = request.POST.get("activate", "0")
|
||||||
|
|
||||||
|
if activate == "1":
|
||||||
|
# Enable GCal coupling
|
||||||
|
email = request.POST.get("email", "")
|
||||||
|
if email:
|
||||||
UserGCalCoupling.objects.filter(user=request.user).delete()
|
UserGCalCoupling.objects.filter(user=request.user).delete()
|
||||||
c = UserGCalCoupling(user=request.user, email=request.POST['email'])
|
coupling = UserGCalCoupling(user=request.user, email=email)
|
||||||
c.save()
|
coupling.save()
|
||||||
sync_from_local_to_google()
|
sync_from_local_to_google()
|
||||||
else:
|
else:
|
||||||
|
# Disable GCal coupling
|
||||||
UserGCalCoupling.objects.filter(user=request.user).delete()
|
UserGCalCoupling.objects.filter(user=request.user).delete()
|
||||||
sync_from_local_to_google()
|
sync_from_local_to_google()
|
||||||
|
|
||||||
context = {}
|
context = {}
|
||||||
user_coupling = UserGCalCoupling.objects.filter(user=request.user)
|
user_coupling = UserGCalCoupling.objects.filter(user=request.user)
|
||||||
context['enabled'] = len(user_coupling)
|
context["enabled"] = user_coupling.exists()
|
||||||
assert (len(user_coupling) < 2)
|
|
||||||
if len(user_coupling) == 1:
|
|
||||||
context['mail'] = user_coupling[0].email
|
|
||||||
|
|
||||||
return render(request, 'eventplanner_gcal/management.html', context)
|
if user_coupling.count() > 1:
|
||||||
|
logger.warning(
|
||||||
|
f"User {request.user.username} has multiple GCal couplings. "
|
||||||
|
"This should not happen."
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_coupling.exists():
|
||||||
|
context["mail"] = user_coupling.first().email
|
||||||
|
|
||||||
|
return render(request, "eventplanner_gcal/management.html", context)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def gcal_api_callback(request):
|
def gcal_api_callback(request):
|
||||||
token = ""
|
"""
|
||||||
if 'HTTP_X_GOOG_CHANNEL_TOKEN' in request.META:
|
Callback endpoint for Google Calendar push notifications.
|
||||||
token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
|
|
||||||
|
|
||||||
channel_id = ""
|
This is called by Google when calendar events are updated.
|
||||||
if 'HTTP_X_GOOG_CHANNEL_ID' in request.META:
|
"""
|
||||||
channel_id = request.META['HTTP_X_GOOG_CHANNEL_ID']
|
token = request.META.get("HTTP_X_GOOG_CHANNEL_TOKEN", "")
|
||||||
|
channel_id = request.META.get("HTTP_X_GOOG_CHANNEL_ID", "")
|
||||||
resource_id = ""
|
resource_id = request.META.get("HTTP_X_GOOG_RESOURCE_ID", "")
|
||||||
if 'HTTP_X_GOOG_RESOURCE_ID' in request.META:
|
|
||||||
resource_id = request.META['HTTP_X_GOOG_RESOURCE_ID']
|
|
||||||
|
|
||||||
valid = check_if_google_callback_is_valid(token, channel_id, resource_id)
|
valid = check_if_google_callback_is_valid(token, channel_id, resource_id)
|
||||||
|
|
||||||
if not valid:
|
if not valid:
|
||||||
return HttpResponse('<h1>Old Channel - no update triggered</h1>')
|
logger.warning(
|
||||||
|
f"Received invalid GCal callback: token={token}, "
|
||||||
|
f"channel_id={channel_id}, resource_id={resource_id}"
|
||||||
|
)
|
||||||
|
return HttpResponse("<h1>Old Channel - no update triggered</h1>")
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Received Google Callback with headers - "
|
||||||
|
f"Token: {token}, ID: {channel_id}, ResID: {resource_id}"
|
||||||
|
)
|
||||||
|
|
||||||
logger.info("Received Google Callback with the following headers Token: "
|
|
||||||
"%s ID %s ResID %s " % (token, channel_id, resource_id))
|
|
||||||
result = sync_from_google_to_local()
|
result = sync_from_google_to_local()
|
||||||
logger.info("Finished processing callback from GCal - New Information present: %d " % (result,))
|
|
||||||
return HttpResponse('<h1>Callback successful</h1>')
|
logger.info(
|
||||||
|
f"Finished processing callback from GCal - New Information present: {result}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponse("<h1>Callback successful</h1>")
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards compatibility aliases
|
||||||
|
runSync = run_sync
|
||||||
|
gcalApiCallback = gcal_api_callback
|
||||||
|
|||||||
0
imagestore/__init__.py
Normal file
36
imagestore/admin.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from imagestore.models import Image, Album, AlbumUpload
|
||||||
|
from sorl.thumbnail.admin import AdminInlineImageMixin
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class InlineImageAdmin(AdminInlineImageMixin, admin.TabularInline):
|
||||||
|
model = Image
|
||||||
|
fieldsets = ((None, {'fields': ['image', 'user', 'title', 'order', 'tags', 'album']}),)
|
||||||
|
raw_id_fields = ('user', )
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
class AlbumAdmin(admin.ModelAdmin):
|
||||||
|
fieldsets = ((None, {'fields': ['name', 'user', 'is_public', 'order']}),)
|
||||||
|
list_display = ('name', 'admin_thumbnail', 'user', 'created', 'updated', 'is_public', 'order')
|
||||||
|
list_editable = ('order', )
|
||||||
|
inlines = [InlineImageAdmin]
|
||||||
|
|
||||||
|
admin.site.register(Album, AlbumAdmin)
|
||||||
|
|
||||||
|
class ImageAdmin(admin.ModelAdmin):
|
||||||
|
fieldsets = ((None, {'fields': ['user', 'title', 'image', 'description', 'order', 'tags', 'album']}),)
|
||||||
|
list_display = ('admin_thumbnail', 'user', 'order', 'album', 'title')
|
||||||
|
raw_id_fields = ('user', )
|
||||||
|
list_filter = ('album', )
|
||||||
|
|
||||||
|
class AlbumUploadAdmin(admin.ModelAdmin):
|
||||||
|
def has_change_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
IMAGE_MODEL = getattr(settings, 'IMAGESTORE_IMAGE_MODEL', None)
|
||||||
|
if not IMAGE_MODEL:
|
||||||
|
admin.site.register(Image, ImageAdmin)
|
||||||
|
|
||||||
|
ALBUM_MODEL = getattr(settings, 'IMAGESTORE_ALBUM_MODEL', None)
|
||||||
|
if not ALBUM_MODEL:
|
||||||
|
admin.site.register(AlbumUpload, AlbumUploadAdmin)
|
||||||
9
imagestore/autocomplete_light_registry.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import autocomplete_light
|
||||||
|
from tagging.models import Tag
|
||||||
|
|
||||||
|
autocomplete_light.register(
|
||||||
|
Tag,
|
||||||
|
search_fields=['^name']
|
||||||
|
)
|
||||||
29
imagestore/context_processors.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
|
from django.conf import settings
|
||||||
|
from utils import get_model_string
|
||||||
|
from imagestore.models import image_applabel, image_classname
|
||||||
|
from imagestore.models import album_applabel, album_classname
|
||||||
|
|
||||||
|
def imagestore_processor(request):
|
||||||
|
template = getattr(settings, 'IMAGESTORE_TEMPLATE', False)
|
||||||
|
ret = {
|
||||||
|
'IMAGESTORE_SHOW_USER': getattr(settings, 'IMAGESTORE_SHOW_USER', True),
|
||||||
|
'IMAGESTORE_SHOW_TAGS': getattr(settings, 'IMAGESTORE_SHOW_TAGS', True),
|
||||||
|
'IMAGESTORE_MODEL_STRING': get_model_string('Image'),
|
||||||
|
'IMAGESTORE_LOAD_CSS': getattr(settings, 'IMAGESTORE_LOAD_CSS', True),
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
ret['imagestore_index_url'] = reverse('imagestore:index')
|
||||||
|
except NoReverseMatch: #Bastard django-cms from hell!!!!111
|
||||||
|
pass
|
||||||
|
if template:
|
||||||
|
ret['IMAGESTORE_TEMPLATE'] = template
|
||||||
|
ret['imagestore_perms'] = {
|
||||||
|
'add_image': request.user.has_perm('%s.add_%s' % (image_applabel, image_classname)),
|
||||||
|
'add_album': request.user.has_perm('%s.add_%s' % (album_applabel, album_classname)),
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
42
imagestore/forms.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
try:
|
||||||
|
import autocomplete_light
|
||||||
|
AUTOCOMPLETE_LIGHT_INSTALLED = True
|
||||||
|
except ImportError:
|
||||||
|
AUTOCOMPLETE_LIGHT_INSTALLED = False
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from models import Image, Album
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class ImageForm(forms.ModelForm):
|
||||||
|
class Meta(object):
|
||||||
|
model = Image
|
||||||
|
exclude = ('user', 'order')
|
||||||
|
|
||||||
|
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 2, 'cols': 19}), required=False,
|
||||||
|
label=_('Description'))
|
||||||
|
|
||||||
|
def __init__(self, user, *args, **kwargs):
|
||||||
|
super(ImageForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['album'].queryset = Album.objects.filter(user=user)
|
||||||
|
self.fields['album'].required = True
|
||||||
|
if AUTOCOMPLETE_LIGHT_INSTALLED:
|
||||||
|
self.fields['tags'].widget = autocomplete_light.TextWidget('TagAutocomplete')
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumForm(forms.ModelForm):
|
||||||
|
class Meta(object):
|
||||||
|
model = Album
|
||||||
|
exclude = ('user', 'created', 'updated')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(AlbumForm, self).__init__(*args, **kwargs)
|
||||||
|
if 'instance' in kwargs and kwargs['instance']:
|
||||||
|
self.fields['head'].queryset = Image.objects.filter(album=kwargs['instance'])
|
||||||
|
else:
|
||||||
|
self.fields['head'].widget = forms.HiddenInput()
|
||||||
5
imagestore/imagestore_cms/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
15
imagestore/imagestore_cms/cms_app.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from cms.app_base import CMSApp
|
||||||
|
from cms.apphook_pool import apphook_pool
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
class ImagestoreApp(CMSApp):
|
||||||
|
name = _("Imagestore App") # give your app a name, this is required
|
||||||
|
urls = ["imagestore.imagestore_cms.urls"] # link your app to url configuration(s)
|
||||||
|
|
||||||
|
apphook_pool.register(ImagestoreApp) # register your app
|
||||||
|
|
||||||
53
imagestore/imagestore_cms/cms_plugins.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from cms.plugin_base import CMSPluginBase
|
||||||
|
from cms.plugin_pool import plugin_pool
|
||||||
|
from models import ImagestoreAlbumPtr, ImagestoreAlbumCarousel
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class AlbumPlugin(CMSPluginBase):
|
||||||
|
model = ImagestoreAlbumPtr
|
||||||
|
name = _('Album')
|
||||||
|
render_template = "cms/plugins/imagestore_album.html"
|
||||||
|
text_enabled = True
|
||||||
|
|
||||||
|
def render(self, context, instance, placeholder):
|
||||||
|
context.update({'album': instance.album})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumCarouselPlugin(CMSPluginBase):
|
||||||
|
model = ImagestoreAlbumCarousel
|
||||||
|
name = _('Album as carousel')
|
||||||
|
render_template = "cms/plugins/imagestore_album_carousel.html"
|
||||||
|
text_enabled = True
|
||||||
|
|
||||||
|
def render(self, context, instance, placeholder):
|
||||||
|
|
||||||
|
# default carousel template in the settings file
|
||||||
|
carousel_template = getattr(settings, 'IMAGESTORE_CAROUSEL_TEMPLATE', None)
|
||||||
|
|
||||||
|
if carousel_template:
|
||||||
|
self.render_template = carousel_template
|
||||||
|
|
||||||
|
if instance.template_file:
|
||||||
|
self.render_template = instance.template_file
|
||||||
|
else:
|
||||||
|
if carousel_template:
|
||||||
|
instance.template_file = carousel_template
|
||||||
|
else:
|
||||||
|
instance.template_file = self.render_template
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
images = instance.album.images.all()
|
||||||
|
if instance.limit:
|
||||||
|
images = images[:instance.limit]
|
||||||
|
context.update({'images': images, 'carousel': instance})
|
||||||
|
return context
|
||||||
|
|
||||||
|
plugin_pool.register_plugin(AlbumCarouselPlugin)
|
||||||
|
plugin_pool.register_plugin(AlbumPlugin)
|
||||||
112
imagestore/imagestore_cms/migrations/0001_initial.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'ImagestoreAlbumPtr'
|
||||||
|
db.create_table('cmsplugin_imagestorealbumptr', (
|
||||||
|
('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
|
||||||
|
('album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Album'])),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore_cms', ['ImagestoreAlbumPtr'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'ImagestoreAlbumPtr'
|
||||||
|
db.delete_table('cmsplugin_imagestorealbumptr')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'cms.cmsplugin': {
|
||||||
|
'Meta': {'object_name': 'CMSPlugin'},
|
||||||
|
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
|
||||||
|
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||||
|
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'cms.placeholder': {
|
||||||
|
'Meta': {'object_name': 'Placeholder'},
|
||||||
|
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumptr': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumPtr', 'db_table': "'cmsplugin_imagestorealbumptr'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore_cms']
|
||||||
121
imagestore/imagestore_cms/migrations/0002_add_carusel.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'ImagestoreAlbumCarusel'
|
||||||
|
db.create_table('cmsplugin_imagestorealbumcarusel', (
|
||||||
|
('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
|
||||||
|
('album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Album'])),
|
||||||
|
('width', self.gf('django.db.models.fields.IntegerField')(default=200)),
|
||||||
|
('limit', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore_cms', ['ImagestoreAlbumCarusel'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'ImagestoreAlbumCarusel'
|
||||||
|
db.delete_table('cmsplugin_imagestorealbumcarusel')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'cms.cmsplugin': {
|
||||||
|
'Meta': {'object_name': 'CMSPlugin'},
|
||||||
|
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
|
||||||
|
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||||
|
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'cms.placeholder': {
|
||||||
|
'Meta': {'object_name': 'Placeholder'},
|
||||||
|
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumcarusel': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumCarusel', 'db_table': "'cmsplugin_imagestorealbumcarusel'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
|
||||||
|
'limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'width': ('django.db.models.fields.IntegerField', [], {'default': '200'})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumptr': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumPtr', 'db_table': "'cmsplugin_imagestorealbumptr'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore_cms']
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'ImagestoreAlbumCarusel'
|
||||||
|
db.delete_table('cmsplugin_imagestorealbumcarusel')
|
||||||
|
|
||||||
|
# Adding model 'ImagestoreAlbumCarousel'
|
||||||
|
db.create_table('cmsplugin_imagestorealbumcarousel', (
|
||||||
|
('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
|
||||||
|
('album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Album'])),
|
||||||
|
('width', self.gf('django.db.models.fields.IntegerField')(default=200)),
|
||||||
|
('height', self.gf('django.db.models.fields.IntegerField')(default=200)),
|
||||||
|
('skin', self.gf('django.db.models.fields.CharField')(default='jcarousel-skin-tango', max_length=100)),
|
||||||
|
('limit', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore_cms', ['ImagestoreAlbumCarousel'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'ImagestoreAlbumCarusel'
|
||||||
|
db.create_table('cmsplugin_imagestorealbumcarusel', (
|
||||||
|
('album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Album'])),
|
||||||
|
('limit', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
|
||||||
|
('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
|
||||||
|
('width', self.gf('django.db.models.fields.IntegerField')(default=200)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore_cms', ['ImagestoreAlbumCarusel'])
|
||||||
|
|
||||||
|
# Deleting model 'ImagestoreAlbumCarousel'
|
||||||
|
db.delete_table('cmsplugin_imagestorealbumcarousel')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'cms.cmsplugin': {
|
||||||
|
'Meta': {'object_name': 'CMSPlugin'},
|
||||||
|
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
|
||||||
|
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||||
|
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'cms.placeholder': {
|
||||||
|
'Meta': {'object_name': 'Placeholder'},
|
||||||
|
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumcarousel': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumCarousel', 'db_table': "'cmsplugin_imagestorealbumcarousel'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
|
||||||
|
'height': ('django.db.models.fields.IntegerField', [], {'default': '200'}),
|
||||||
|
'limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'skin': ('django.db.models.fields.CharField', [], {'default': "'jcarousel-skin-tango'", 'max_length': '100'}),
|
||||||
|
'width': ('django.db.models.fields.IntegerField', [], {'default': '200'})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumptr': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumPtr', 'db_table': "'cmsplugin_imagestorealbumptr'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore_cms']
|
||||||
135
imagestore/imagestore_cms/migrations/0004_add_carousel_sizes.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'ImagestoreAlbumCarousel.height'
|
||||||
|
db.delete_column('cmsplugin_imagestorealbumcarousel', 'height')
|
||||||
|
|
||||||
|
# Deleting field 'ImagestoreAlbumCarousel.width'
|
||||||
|
db.delete_column('cmsplugin_imagestorealbumcarousel', 'width')
|
||||||
|
|
||||||
|
# Adding field 'ImagestoreAlbumCarousel.size'
|
||||||
|
db.add_column('cmsplugin_imagestorealbumcarousel', 'size', self.gf('django.db.models.fields.CharField')(default='72x72', max_length=20), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'ImagestoreAlbumCarousel.full_size'
|
||||||
|
db.add_column('cmsplugin_imagestorealbumcarousel', 'full_size', self.gf('django.db.models.fields.CharField')(default='600x600', max_length=20), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'ImagestoreAlbumCarousel.height'
|
||||||
|
db.add_column('cmsplugin_imagestorealbumcarousel', 'height', self.gf('django.db.models.fields.IntegerField')(default=200), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'ImagestoreAlbumCarousel.width'
|
||||||
|
db.add_column('cmsplugin_imagestorealbumcarousel', 'width', self.gf('django.db.models.fields.IntegerField')(default=200), keep_default=False)
|
||||||
|
|
||||||
|
# Deleting field 'ImagestoreAlbumCarousel.size'
|
||||||
|
db.delete_column('cmsplugin_imagestorealbumcarousel', 'size')
|
||||||
|
|
||||||
|
# Deleting field 'ImagestoreAlbumCarousel.full_size'
|
||||||
|
db.delete_column('cmsplugin_imagestorealbumcarousel', 'full_size')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'cms.cmsplugin': {
|
||||||
|
'Meta': {'object_name': 'CMSPlugin'},
|
||||||
|
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
|
||||||
|
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||||
|
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'cms.placeholder': {
|
||||||
|
'Meta': {'object_name': 'Placeholder'},
|
||||||
|
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumcarousel': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumCarousel', 'db_table': "'cmsplugin_imagestorealbumcarousel'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
|
||||||
|
'full_size': ('django.db.models.fields.CharField', [], {'default': "'600x600'", 'max_length': '20'}),
|
||||||
|
'limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'size': ('django.db.models.fields.CharField', [], {'default': "'72x72'", 'max_length': '20'}),
|
||||||
|
'skin': ('django.db.models.fields.CharField', [], {'default': "'jcarousel-skin-tango'", 'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumptr': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumPtr', 'db_table': "'cmsplugin_imagestorealbumptr'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore_cms']
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'ImagestoreAlbumCarousel.template_file'
|
||||||
|
db.add_column('cmsplugin_imagestorealbumcarousel', 'template_file', self.gf('django.db.models.fields.CharField')(default='cms/plugins/imagestore_album_carousel.html', max_length=100), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'ImagestoreAlbumCarousel.template_file'
|
||||||
|
db.delete_column('cmsplugin_imagestorealbumcarousel', 'template_file')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'cms.cmsplugin': {
|
||||||
|
'Meta': {'object_name': 'CMSPlugin'},
|
||||||
|
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
|
||||||
|
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
|
||||||
|
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'cms.placeholder': {
|
||||||
|
'Meta': {'object_name': 'Placeholder'},
|
||||||
|
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'image.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image', 'db_table': "'imagestore_image'"},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'place': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['places.GeoPlace']"}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['image.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumcarousel': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumCarousel', 'db_table': "'cmsplugin_imagestorealbumcarousel'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
|
||||||
|
'full_size': ('django.db.models.fields.CharField', [], {'default': "'600x600'", 'max_length': '20'}),
|
||||||
|
'limit': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'size': ('django.db.models.fields.CharField', [], {'default': "'72x72'", 'max_length': '20'}),
|
||||||
|
'skin': ('django.db.models.fields.CharField', [], {'default': "'jcarousel-skin-tango'", 'max_length': '100'}),
|
||||||
|
'template_file': ('django.db.models.fields.CharField', [], {'default': "'cms/plugins/imagestore_album_carousel.html'", 'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore_cms.imagestorealbumptr': {
|
||||||
|
'Meta': {'object_name': 'ImagestoreAlbumPtr', 'db_table': "'cmsplugin_imagestorealbumptr'", '_ormbases': ['cms.CMSPlugin']},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']"}),
|
||||||
|
'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
|
||||||
|
},
|
||||||
|
'places.geoplace': {
|
||||||
|
'Meta': {'object_name': 'GeoPlace'},
|
||||||
|
'addional_info': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'address': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'imagestore_tag': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'longtitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'metro': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'minuses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'near_objects': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'near_objects_rel_+'", 'null': 'True', 'to': "orm['places.GeoPlace']"}),
|
||||||
|
'near_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'path_to': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'phone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'pluses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'private': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Topic']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'topic_on_demand': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['places.PlaceType']", 'null': 'None', 'blank': 'None'}),
|
||||||
|
'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'work_time': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'places.placetype': {
|
||||||
|
'Meta': {'object_name': 'PlaceType'},
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Forum']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'forum_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'icon_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'name_plural': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'path_to_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.category': {
|
||||||
|
'Meta': {'ordering': "['position']", 'object_name': 'Category'},
|
||||||
|
'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||||
|
'position': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.forum': {
|
||||||
|
'Meta': {'ordering': "['position']", 'object_name': 'Forum'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'forums'", 'to': "orm['pybb.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||||
|
'headline': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'moderators': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||||
|
'position': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'post_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'readed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'readed_forums'", 'symmetrical': 'False', 'through': "orm['pybb.ForumReadTracker']", 'to': "orm['auth.User']"}),
|
||||||
|
'topic_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.forumreadtracker': {
|
||||||
|
'Meta': {'object_name': 'ForumReadTracker'},
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Forum']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'time_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'pybb.topic': {
|
||||||
|
'Meta': {'ordering': "['-created']", 'object_name': 'Topic'},
|
||||||
|
'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'topics'", 'to': "orm['pybb.Forum']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'on_moderation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'post_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'readed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'readed_topics'", 'symmetrical': 'False', 'through': "orm['pybb.TopicReadTracker']", 'to': "orm['auth.User']"}),
|
||||||
|
'sticky': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'subscriptions'", 'blank': 'True', 'to': "orm['auth.User']"}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||||
|
'views': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.topicreadtracker': {
|
||||||
|
'Meta': {'object_name': 'TopicReadTracker'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'time_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Topic']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore_cms']
|
||||||
0
imagestore/imagestore_cms/migrations/__init__.py
Normal file
21
imagestore/imagestore_cms/models.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from cms.models import CMSPlugin
|
||||||
|
from django.db import models
|
||||||
|
from imagestore.models import Album
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class ImagestoreAlbumPtr(CMSPlugin):
|
||||||
|
album = models.ForeignKey(Album, verbose_name=_('Album'), blank=False, null=False)
|
||||||
|
|
||||||
|
class ImagestoreAlbumCarousel(CMSPlugin):
|
||||||
|
album = models.ForeignKey(Album, verbose_name=_('Album'), blank=False, null=False)
|
||||||
|
skin = models.CharField(max_length=100, verbose_name=_('Skin'), default='jcarousel-skin-tango')
|
||||||
|
limit = models.IntegerField(verbose_name=_('Image limit'), blank=True, null=True)
|
||||||
|
size = models.CharField(max_length=20, verbose_name=_('Thumbnail size'), default='72x72')
|
||||||
|
full_size = models.CharField(max_length=20, verbose_name=_('Full size view'), default='600x600')
|
||||||
|
template_file = models.CharField(max_length=100, verbose_name=_('Template file'), default=getattr(settings,'IMAGESTORE_CAROUSEL_TEMPLATE','cms/plugins/imagestore_album_carousel.html'), blank=True, null=True)
|
||||||
11
imagestore/imagestore_cms/urls.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
from imagestore.views import AlbumListView
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^', include('imagestore.urls', namespace='imagestore')),
|
||||||
|
)
|
||||||
BIN
imagestore/locale/de/LC_MESSAGES/django.mo
Normal file
276
imagestore/locale/de/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2013-09-29 16:54+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: forms.py:22 models/bases/image.py:44
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
|
#: views.py:63
|
||||||
|
#, python-format
|
||||||
|
msgid "No Tag found matching \"%s\"."
|
||||||
|
msgstr "Kein Tag gefunden der auf \"%s\" passt."
|
||||||
|
|
||||||
|
#: imagestore_cms/cms_app.py:11
|
||||||
|
msgid "Imagestore App"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: imagestore_cms/cms_plugins.py:14 imagestore_cms/models.py:13
|
||||||
|
#: imagestore_cms/models.py:16 models/album.py:14 models/bases/image.py:51
|
||||||
|
#: templates/imagestore/image-scope.html:9 templates/imagestore/image.html:16
|
||||||
|
msgid "Album"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: imagestore_cms/cms_plugins.py:25
|
||||||
|
msgid "Album as carousel"
|
||||||
|
msgstr "Album als Karousel"
|
||||||
|
|
||||||
|
#: imagestore_cms/models.py:17
|
||||||
|
msgid "Skin"
|
||||||
|
msgstr "Skin"
|
||||||
|
|
||||||
|
#: imagestore_cms/models.py:18
|
||||||
|
msgid "Image limit"
|
||||||
|
msgstr "Bilder Limit"
|
||||||
|
|
||||||
|
#: imagestore_cms/models.py:19
|
||||||
|
msgid "Thumbnail size"
|
||||||
|
msgstr "Thumbnail Größe"
|
||||||
|
|
||||||
|
#: imagestore_cms/models.py:20
|
||||||
|
msgid "Full size view"
|
||||||
|
msgstr "Vollbild Ansicht"
|
||||||
|
|
||||||
|
#: imagestore_cms/models.py:21
|
||||||
|
msgid "Template file"
|
||||||
|
msgstr "Template File"
|
||||||
|
|
||||||
|
#: models/album.py:15
|
||||||
|
msgid "Albums"
|
||||||
|
msgstr "Alben"
|
||||||
|
|
||||||
|
#: models/image.py:13 templates/imagestore/image.html:8
|
||||||
|
msgid "Image"
|
||||||
|
msgstr "Bild"
|
||||||
|
|
||||||
|
#: models/image.py:14
|
||||||
|
msgid "Images"
|
||||||
|
msgstr "Bilder"
|
||||||
|
|
||||||
|
#: models/upload.py:84
|
||||||
|
msgid "images file (.zip)"
|
||||||
|
msgstr "Bild Datei (.zip)"
|
||||||
|
|
||||||
|
#: models/upload.py:85
|
||||||
|
msgid "Select a .zip file of images to upload into a new Gallery."
|
||||||
|
msgstr "Zip Datei mit Bilder auswählen um eine neue Gallerie zu erstellen."
|
||||||
|
|
||||||
|
#: models/upload.py:90
|
||||||
|
msgid ""
|
||||||
|
"Select an album to add these images to. leave this empty to create a new "
|
||||||
|
"album from the supplied title."
|
||||||
|
msgstr "Bitte Album für neue Bilder auswählen. Leer lassen um ein neues Album zu erstellen."
|
||||||
|
|
||||||
|
#: models/upload.py:95
|
||||||
|
msgid "New album name"
|
||||||
|
msgstr "Name für neues Album"
|
||||||
|
|
||||||
|
#: models/upload.py:96
|
||||||
|
msgid ""
|
||||||
|
"If not empty new album with this name will be created and images will be "
|
||||||
|
"upload to this album"
|
||||||
|
msgstr "Falls nicht leer wird ein Album mit diesem Namen erstellt, und hochgeladene Bilder werden in dieses Album hinzugefügt."
|
||||||
|
|
||||||
|
#: models/upload.py:98
|
||||||
|
msgid "tags"
|
||||||
|
msgstr "Tags"
|
||||||
|
|
||||||
|
#: models/upload.py:101
|
||||||
|
msgid "Album upload"
|
||||||
|
msgstr "Album upload"
|
||||||
|
|
||||||
|
#: models/upload.py:102
|
||||||
|
msgid "Album uploads"
|
||||||
|
msgstr "Album uploads"
|
||||||
|
|
||||||
|
#: models/bases/album.py:38 models/bases/image.py:48
|
||||||
|
#: templates/imagestore/image-scope.html:4 templates/imagestore/image.html:13
|
||||||
|
#: templates/imagestore/user_info.html:7
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Benutzer"
|
||||||
|
|
||||||
|
#: models/bases/album.py:39
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: models/bases/album.py:40 models/bases/image.py:49
|
||||||
|
msgid "Created"
|
||||||
|
msgstr "Erstellt"
|
||||||
|
|
||||||
|
#: models/bases/album.py:41 models/bases/image.py:50
|
||||||
|
msgid "Updated"
|
||||||
|
msgstr "Aktualisiert"
|
||||||
|
|
||||||
|
#: models/bases/album.py:42
|
||||||
|
msgid "Is public"
|
||||||
|
msgstr "Öffentlich"
|
||||||
|
|
||||||
|
#: models/bases/album.py:45 models/bases/image.py:46
|
||||||
|
msgid "Order"
|
||||||
|
msgstr "Reihenfolge"
|
||||||
|
|
||||||
|
#: models/bases/album.py:72 templates/imagestore/album_list.html:53
|
||||||
|
msgid "Empty album"
|
||||||
|
msgstr "Leeres Album"
|
||||||
|
|
||||||
|
#: models/bases/album.py:74
|
||||||
|
msgid "Head"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models/bases/image.py:43
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Titel"
|
||||||
|
|
||||||
|
#: models/bases/image.py:45 templates/imagestore/image.html:77
|
||||||
|
#: templates/imagestore/tag-cloud.html:7
|
||||||
|
msgid "Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: models/bases/image.py:47
|
||||||
|
msgid "File"
|
||||||
|
msgstr "Datei"
|
||||||
|
|
||||||
|
#: models/bases/image.py:68
|
||||||
|
msgid "Thumbnail"
|
||||||
|
msgstr "Vorschau"
|
||||||
|
|
||||||
|
#: templates/imagestore/album_delete.html:6
|
||||||
|
msgid "Are you sure that you would like to delete this album?"
|
||||||
|
msgstr "Dieses Album wirklich löschen?"
|
||||||
|
|
||||||
|
#: templates/imagestore/album_delete.html:10
|
||||||
|
#: templates/imagestore/image_confirm_delete.html:10
|
||||||
|
#: templates/imagestore/image_delete.html:10
|
||||||
|
msgid "No, take me back"
|
||||||
|
msgstr "Nein, zurück"
|
||||||
|
|
||||||
|
#: templates/imagestore/album_delete.html:11
|
||||||
|
#: templates/imagestore/image_confirm_delete.html:11
|
||||||
|
#: templates/imagestore/image_delete.html:11
|
||||||
|
msgid "Yes, I am sure"
|
||||||
|
msgstr "Ja"
|
||||||
|
|
||||||
|
#: templates/imagestore/album_list.html:7
|
||||||
|
#: templates/imagestore/album_list.html:16
|
||||||
|
#: templates/imagestore/album_list.html:37
|
||||||
|
msgid "Albums for user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/imagestore/album_list.html:9
|
||||||
|
#: templates/imagestore/album_list.html:18
|
||||||
|
#: templates/imagestore/album_list.html:39
|
||||||
|
msgid "All albums"
|
||||||
|
msgstr "Alle Alben"
|
||||||
|
|
||||||
|
#: templates/imagestore/album_list.html:60
|
||||||
|
msgid "user"
|
||||||
|
msgstr "Benutzer"
|
||||||
|
|
||||||
|
#: templates/imagestore/base.html:27
|
||||||
|
msgid "Home"
|
||||||
|
msgstr "Home"
|
||||||
|
|
||||||
|
#: templates/imagestore/base.html:30 templates/imagestore/tag.html:7
|
||||||
|
msgid "Gallery"
|
||||||
|
msgstr "Galerie"
|
||||||
|
|
||||||
|
#: templates/imagestore/base.html:46
|
||||||
|
#: templates/imagestore/forms/image_form.html:7
|
||||||
|
#: templates/imagestore/forms/image_form.html:11
|
||||||
|
#: templates/imagestore/forms/image_form.html:15
|
||||||
|
msgid "Upload image"
|
||||||
|
msgstr "Bild hochladen"
|
||||||
|
|
||||||
|
#: templates/imagestore/base.html:49
|
||||||
|
msgid "Create new album"
|
||||||
|
msgstr "Neues Album erstellen"
|
||||||
|
|
||||||
|
#: templates/imagestore/image-list.html:14
|
||||||
|
#: templates/imagestore/image_list.html:55
|
||||||
|
msgid "Info"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/imagestore/image-scope.html:14 templates/imagestore/image.html:19
|
||||||
|
#: templates/imagestore/tag.html:7 templates/imagestore/tag.html.py:11
|
||||||
|
#: templates/imagestore/tag.html:15
|
||||||
|
msgid "Tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/imagestore/image.html:56
|
||||||
|
msgid "previous image"
|
||||||
|
msgstr "Vorheriges Bild"
|
||||||
|
|
||||||
|
#: templates/imagestore/image.html:59
|
||||||
|
msgid "next image"
|
||||||
|
msgstr "Nächstes Bild"
|
||||||
|
|
||||||
|
#: templates/imagestore/image.html:69
|
||||||
|
msgid "Edit info"
|
||||||
|
msgstr "Info editieren"
|
||||||
|
|
||||||
|
#: templates/imagestore/image.html:70
|
||||||
|
msgid "Delete image"
|
||||||
|
msgstr "Bild löschen"
|
||||||
|
|
||||||
|
#: templates/imagestore/image.html:85
|
||||||
|
msgid "Place"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/imagestore/image_confirm_delete.html:6
|
||||||
|
#: templates/imagestore/image_delete.html:6
|
||||||
|
msgid "Are you sure that you would like to delete this image?"
|
||||||
|
msgstr "Soll das Bild sicher gelöscht werden?"
|
||||||
|
|
||||||
|
#: templates/imagestore/image_list.html:34
|
||||||
|
#: templates/imagestore/forms/album_form.html:13
|
||||||
|
#: templates/imagestore/forms/album_form.html:23
|
||||||
|
msgid "Edit album"
|
||||||
|
msgstr "Album bearbeiten"
|
||||||
|
|
||||||
|
#: templates/imagestore/pagination.html:8
|
||||||
|
msgid "previous page"
|
||||||
|
msgstr "Vorherige Seite"
|
||||||
|
|
||||||
|
#: templates/imagestore/pagination.html:22
|
||||||
|
msgid "next page"
|
||||||
|
msgstr "Nächste Seite"
|
||||||
|
|
||||||
|
#: templates/imagestore/forms/album_form.html:7
|
||||||
|
#: templates/imagestore/forms/album_form.html:15
|
||||||
|
#: templates/imagestore/forms/album_form.html:25
|
||||||
|
msgid "Create album"
|
||||||
|
msgstr "Neues album"
|
||||||
|
|
||||||
|
#: templates/imagestore/forms/album_form.html:31
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Speichern"
|
||||||
|
|
||||||
|
#: templates/imagestore/forms/image_form.html:19
|
||||||
|
msgid "Upload"
|
||||||
|
msgstr "Hochladen"
|
||||||
78
imagestore/migrations/0001_initial.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'Category'
|
||||||
|
db.create_table('imagestore_category', (
|
||||||
|
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['imagestore.Category'])),
|
||||||
|
('slug', self.gf('django.db.models.fields.SlugField')(max_length=200, db_index=True)),
|
||||||
|
('title', self.gf('django.db.models.fields.CharField')(max_length=200)),
|
||||||
|
('order', self.gf('django.db.models.fields.IntegerField')()),
|
||||||
|
('is_public', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||||
|
('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore', ['Category'])
|
||||||
|
|
||||||
|
# Adding model 'Image'
|
||||||
|
db.create_table('imagestore_image', (
|
||||||
|
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=200, null=True, blank=True)),
|
||||||
|
('title', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)),
|
||||||
|
('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
|
||||||
|
('tags', self.gf('tagging.fields.TagField')()),
|
||||||
|
('category', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Category'])),
|
||||||
|
('order', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
|
||||||
|
('is_public', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||||
|
('image', self.gf('sorl.thumbnail.fields.ImageField')(max_length=100)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore', ['Image'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'Category'
|
||||||
|
db.delete_table('imagestore_category')
|
||||||
|
|
||||||
|
# Deleting model 'Image'
|
||||||
|
db.delete_table('imagestore_image')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
48
imagestore/migrations/0002_removeslug.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'Image.slug'
|
||||||
|
db.delete_column('imagestore_image', 'slug')
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'Image.slug'
|
||||||
|
db.add_column('imagestore_image', 'slug', self.gf('django.db.models.fields.SlugField')(blank=True, max_length=200, null=True, db_index=True), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'ordering': "('order', 'title')", 'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'images'", 'to': "orm['imagestore.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
85
imagestore/migrations/0003_adduser.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'Image.user'
|
||||||
|
db.add_column('imagestore_image', 'user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='images', null=True, to=orm['auth.User']), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'Image.user'
|
||||||
|
db.delete_column('imagestore_image', 'user_id')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'ordering': "('order', 'title')", 'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'images'", 'to': "orm['imagestore.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
85
imagestore/migrations/0004_nonullorder.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Image.order'
|
||||||
|
db.alter_column('imagestore_image', 'order', self.gf('django.db.models.fields.IntegerField')())
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Image.order'
|
||||||
|
db.alter_column('imagestore_image', 'order', self.gf('django.db.models.fields.IntegerField')(null=True))
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'ordering': "('order', 'title')", 'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'images'", 'to': "orm['imagestore.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
98
imagestore/migrations/0005_addalbum.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'Album'
|
||||||
|
db.create_table('imagestore_album', (
|
||||||
|
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
|
||||||
|
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||||
|
('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore', ['Album'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'Album'
|
||||||
|
db.delete_table('imagestore_album')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'ordering': "('order', 'title')", 'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'images'", 'to': "orm['imagestore.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
106
imagestore/migrations/0006_addcreatedupdated.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'Image.created'
|
||||||
|
db.add_column('imagestore_image', 'created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True, blank=True), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'Image.updated'
|
||||||
|
db.add_column('imagestore_image', 'updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, null=True, blank=True), keep_default=False)
|
||||||
|
|
||||||
|
# Changing field 'Image.category'
|
||||||
|
db.alter_column('imagestore_image', 'category_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['imagestore.Category']))
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'Image.created'
|
||||||
|
db.delete_column('imagestore_image', 'created')
|
||||||
|
|
||||||
|
# Deleting field 'Image.updated'
|
||||||
|
db.delete_column('imagestore_image', 'updated')
|
||||||
|
|
||||||
|
# User chose to not deal with backwards NULL issues for 'Image.category'
|
||||||
|
raise RuntimeError("Cannot reverse this migration. 'Image.category' and its values cannot be restored.")
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.category': {
|
||||||
|
'Meta': {'ordering': "('order', 'title')", 'object_name': 'Category'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'db_index': 'True'}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Category']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
131
imagestore/migrations/0007_albumfix.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'Category'
|
||||||
|
db.delete_table('imagestore_category')
|
||||||
|
|
||||||
|
# Adding field 'Album.user'
|
||||||
|
db.add_column('imagestore_album', 'user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='albums', null=True, to=orm['auth.User']), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'Album.is_public'
|
||||||
|
db.add_column('imagestore_album', 'is_public', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'Album.head'
|
||||||
|
db.add_column('imagestore_album', 'head', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='head_of', null=True, to=orm['imagestore.Image']), keep_default=False)
|
||||||
|
|
||||||
|
# Deleting field 'Image.is_public'
|
||||||
|
db.delete_column('imagestore_image', 'is_public')
|
||||||
|
|
||||||
|
# Deleting field 'Image.category'
|
||||||
|
db.delete_column('imagestore_image', 'category_id')
|
||||||
|
|
||||||
|
# Adding field 'Image.album'
|
||||||
|
db.add_column('imagestore_image', 'album', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='images', null=True, to=orm['imagestore.Album']), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'Category'
|
||||||
|
db.create_table('imagestore_category', (
|
||||||
|
('rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name='children', null=True, to=orm['imagestore.Category'], blank=True)),
|
||||||
|
('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('is_public', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||||
|
('slug', self.gf('django.db.models.fields.SlugField')(max_length=200, db_index=True)),
|
||||||
|
('level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('title', self.gf('django.db.models.fields.CharField')(max_length=200)),
|
||||||
|
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
|
||||||
|
('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore', ['Category'])
|
||||||
|
|
||||||
|
# Deleting field 'Album.user'
|
||||||
|
db.delete_column('imagestore_album', 'user_id')
|
||||||
|
|
||||||
|
# Deleting field 'Album.is_public'
|
||||||
|
db.delete_column('imagestore_album', 'is_public')
|
||||||
|
|
||||||
|
# Deleting field 'Album.head'
|
||||||
|
db.delete_column('imagestore_album', 'head_id')
|
||||||
|
|
||||||
|
# Adding field 'Image.is_public'
|
||||||
|
db.add_column('imagestore_image', 'is_public', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
|
||||||
|
|
||||||
|
# Adding field 'Image.category'
|
||||||
|
db.add_column('imagestore_image', 'category', self.gf('django.db.models.fields.related.ForeignKey')(related_name='images', null=True, to=orm['imagestore.Category'], blank=True), keep_default=False)
|
||||||
|
|
||||||
|
# Deleting field 'Image.album'
|
||||||
|
db.delete_column('imagestore_image', 'album_id')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
93
imagestore/migrations/0008_permissions.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import DataMigration
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.management import create_permissions
|
||||||
|
from django.contrib.auth.models import User, Permission
|
||||||
|
from django.db.models import get_app
|
||||||
|
|
||||||
|
class Migration(DataMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
app = get_app('imagestore')
|
||||||
|
create_permissions(app, (), 2)
|
||||||
|
add_image_permission = Permission.objects.get_by_natural_key('add_image', 'imagestore', 'image')
|
||||||
|
add_album_permission = Permission.objects.get_by_natural_key('add_album', 'imagestore', 'album')
|
||||||
|
change_image_permission = Permission.objects.get_by_natural_key('change_image', 'imagestore', 'image')
|
||||||
|
change_album_permission = Permission.objects.get_by_natural_key('change_album', 'imagestore', 'album')
|
||||||
|
delete_image_permission = Permission.objects.get_by_natural_key('delete_image', 'imagestore','image')
|
||||||
|
delete_album_permission = Permission.objects.get_by_natural_key('delete_album', 'imagestore', 'album')
|
||||||
|
for user in User.objects.all():
|
||||||
|
user.user_permissions.add(add_image_permission, add_album_permission,)
|
||||||
|
user.user_permissions.add(change_image_permission, change_album_permission,)
|
||||||
|
user.user_permissions.add(delete_image_permission, delete_album_permission,)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
"Write your backwards methods here."
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
89
imagestore/migrations/0009_limits.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Album.name'
|
||||||
|
db.alter_column('imagestore_album', 'name', self.gf('django.db.models.fields.CharField')(max_length=20))
|
||||||
|
|
||||||
|
# Changing field 'Image.title'
|
||||||
|
db.alter_column('imagestore_image', 'title', self.gf('django.db.models.fields.CharField')(max_length=20, null=True))
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Album.name'
|
||||||
|
db.alter_column('imagestore_album', 'name', self.gf('django.db.models.fields.CharField')(max_length=200))
|
||||||
|
|
||||||
|
# Changing field 'Image.title'
|
||||||
|
db.alter_column('imagestore_image', 'title', self.gf('django.db.models.fields.CharField')(max_length=200, null=True))
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
172
imagestore/migrations/0010_addplace.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
try:
|
||||||
|
from places.models import GeoPlace
|
||||||
|
except:
|
||||||
|
GeoPlace = None
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
if GeoPlace:
|
||||||
|
# Adding field 'Image.place'
|
||||||
|
db.add_column('imagestore_image', 'place', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='images', null=True, to=orm['places.GeoPlace']), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'Image.place'
|
||||||
|
db.delete_column('imagestore_image', 'place_id')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'place': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['places.GeoPlace']"}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'places.geoplace': {
|
||||||
|
'Meta': {'object_name': 'GeoPlace'},
|
||||||
|
'addional_info': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'address': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'imagestore_tag': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'longtitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'metro': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'minuses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'near_objects': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'near_objects_rel_+'", 'null': 'True', 'to': "orm['places.GeoPlace']"}),
|
||||||
|
'near_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'path_to': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'phone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'pluses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Topic']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['places.PlaceType']", 'null': 'None', 'blank': 'None'}),
|
||||||
|
'work_time': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'places.placetype': {
|
||||||
|
'Meta': {'object_name': 'PlaceType'},
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Forum']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'forum_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'icon_style': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'name_plural': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'path_to_image': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.category': {
|
||||||
|
'Meta': {'ordering': "['position']", 'object_name': 'Category'},
|
||||||
|
'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||||
|
'position': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.forum': {
|
||||||
|
'Meta': {'ordering': "['position']", 'object_name': 'Forum'},
|
||||||
|
'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'forums'", 'to': "orm['pybb.Category']"}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||||
|
'headline': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'moderators': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||||
|
'position': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'post_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'readed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'readed_forums'", 'symmetrical': 'False', 'through': "orm['pybb.ForumReadTracker']", 'to': "orm['auth.User']"}),
|
||||||
|
'topic_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.forumreadtracker': {
|
||||||
|
'Meta': {'object_name': 'ForumReadTracker'},
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Forum']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'time_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'pybb.topic': {
|
||||||
|
'Meta': {'ordering': "['-created']", 'object_name': 'Topic'},
|
||||||
|
'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||||
|
'forum': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'topics'", 'to': "orm['pybb.Forum']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'post_count': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'}),
|
||||||
|
'readed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'readed_topics'", 'symmetrical': 'False', 'through': "orm['pybb.TopicReadTracker']", 'to': "orm['auth.User']"}),
|
||||||
|
'sticky': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'subscribers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'subscriptions'", 'blank': 'True', 'to': "orm['auth.User']"}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
|
||||||
|
'views': ('django.db.models.fields.IntegerField', [], {'default': '0', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'pybb.topicreadtracker': {
|
||||||
|
'Meta': {'object_name': 'TopicReadTracker'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'time_stamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'topic': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pybb.Topic']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
98
imagestore/migrations/0011_add_mass_upload.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding model 'AlbumUpload'
|
||||||
|
db.create_table('imagestore_albumupload', (
|
||||||
|
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('zip_file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
|
||||||
|
('album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['imagestore.Album'], null=True, blank=True)),
|
||||||
|
('new_album_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||||
|
('tags', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||||
|
))
|
||||||
|
db.send_create_signal('imagestore', ['AlbumUpload'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting model 'AlbumUpload'
|
||||||
|
db.delete_table('imagestore_albumupload')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.albumupload': {
|
||||||
|
'Meta': {'object_name': 'AlbumUpload'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'new_album_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'zip_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
92
imagestore/migrations/0012_add_order.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Adding field 'Album.order'
|
||||||
|
db.add_column('imagestore_album', 'order', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Deleting field 'Album.order'
|
||||||
|
db.delete_column('imagestore_album', 'order')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.albumupload': {
|
||||||
|
'Meta': {'object_name': 'AlbumUpload'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'new_album_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'zip_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
92
imagestore/migrations/0013_fix_album_title_length.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Image.title'
|
||||||
|
db.alter_column('imagestore_image', 'title', self.gf('django.db.models.fields.CharField')(max_length=100, null=True))
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Image.title'
|
||||||
|
db.alter_column('imagestore_image', 'title', self.gf('django.db.models.fields.CharField')(max_length=20, null=True))
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.albumupload': {
|
||||||
|
'Meta': {'object_name': 'AlbumUpload'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'new_album_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'zip_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
92
imagestore/migrations/0014_fix_album_name_length.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Album.name'
|
||||||
|
db.alter_column('imagestore_album', 'name', self.gf('django.db.models.fields.CharField')(max_length=100))
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Album.name'
|
||||||
|
db.alter_column('imagestore_album', 'name', self.gf('django.db.models.fields.CharField')(max_length=20))
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'auth.permission': {
|
||||||
|
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.album': {
|
||||||
|
'Meta': {'ordering': "('created', 'name')", 'object_name': 'Album'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'head': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'head_of'", 'null': 'True', 'to': "orm['imagestore.Image']"}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'albums'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'imagestore.albumupload': {
|
||||||
|
'Meta': {'object_name': 'AlbumUpload'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['imagestore.Album']", 'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'new_album_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||||
|
'zip_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'imagestore.image': {
|
||||||
|
'Meta': {'ordering': "('order', 'id')", 'object_name': 'Image'},
|
||||||
|
'album': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['imagestore.Album']"}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'image': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}),
|
||||||
|
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||||
|
'tags': ('tagging.fields.TagField', [], {}),
|
||||||
|
'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'images'", 'null': 'True', 'to': "orm['auth.User']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['imagestore']
|
||||||
5
imagestore/migrations/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
19
imagestore/models/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
from imagestore.utils import load_class, get_model_string
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
Album = load_class(getattr(settings, 'IMAGESTORE_ALBUM_MODEL', 'imagestore.models.album.Album'))
|
||||||
|
Image = load_class(getattr(settings, 'IMAGESTORE_IMAGE_MODEL', 'imagestore.models.image.Image'))
|
||||||
|
|
||||||
|
# This labels and classnames used to generate permissons labels
|
||||||
|
image_applabel = Image._meta.app_label
|
||||||
|
image_classname = Image.__name__.lower()
|
||||||
|
|
||||||
|
album_applabel = Album._meta.app_label
|
||||||
|
album_classname = Album.__name__.lower()
|
||||||
|
|
||||||
|
|
||||||
|
from upload import AlbumUpload
|
||||||
16
imagestore/models/album.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from bases.album import BaseAlbum
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from imagestore.utils import load_class, get_model_string
|
||||||
|
|
||||||
|
class Album(BaseAlbum):
|
||||||
|
|
||||||
|
class Meta(BaseAlbum.Meta):
|
||||||
|
abstract = False
|
||||||
|
verbose_name = _('Album')
|
||||||
|
verbose_name_plural = _('Albums')
|
||||||
|
app_label = 'imagestore'
|
||||||
5
imagestore/models/bases/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
75
imagestore/models/bases/album.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import permalink
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
from sorl.thumbnail import get_thumbnail
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
User = get_user_model()
|
||||||
|
except ImportError:
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
try:
|
||||||
|
import Image as PILImage
|
||||||
|
except ImportError:
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
|
from imagestore.utils import get_model_string
|
||||||
|
|
||||||
|
|
||||||
|
SELF_MANAGE = getattr(settings, 'IMAGESTORE_SELF_MANAGE', True)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAlbum(models.Model):
|
||||||
|
class Meta(object):
|
||||||
|
abstract = True
|
||||||
|
ordering = ('order', 'created', 'name')
|
||||||
|
permissions = (
|
||||||
|
('moderate_albums', 'View, update and delete any album'),
|
||||||
|
)
|
||||||
|
|
||||||
|
user = models.ForeignKey(User, verbose_name=_('User'), null=True, blank=True, related_name='albums')
|
||||||
|
name = models.CharField(_('Name'), max_length=100, blank=False, null=False)
|
||||||
|
created = models.DateTimeField(_('Created'), auto_now_add=True)
|
||||||
|
updated = models.DateTimeField(_('Updated'), auto_now=True)
|
||||||
|
is_public = models.BooleanField(_('Is public'), default=True)
|
||||||
|
head = models.ForeignKey(get_model_string('Image'), related_name='head_of', null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
order = models.IntegerField(_('Order'), default=0)
|
||||||
|
|
||||||
|
def get_head(self):
|
||||||
|
if self.head:
|
||||||
|
return self.head
|
||||||
|
else:
|
||||||
|
if self.images.all().count()>0:
|
||||||
|
self.head = self.images.all()[0]
|
||||||
|
self.save()
|
||||||
|
return self.head
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@permalink
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return 'imagestore:album', (), {'album_id': self.id}
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def admin_thumbnail(self):
|
||||||
|
img = self.get_head()
|
||||||
|
if img:
|
||||||
|
try:
|
||||||
|
return '<img src="%s">' % get_thumbnail(img.image, '100x100', crop='center').url
|
||||||
|
except IOError:
|
||||||
|
return 'IOError'
|
||||||
|
return _('Empty album')
|
||||||
|
|
||||||
|
admin_thumbnail.short_description = _('Head')
|
||||||
|
admin_thumbnail.allow_tags = True
|
||||||
104
imagestore/models/bases/image.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import permalink
|
||||||
|
from sorl.thumbnail.helpers import ThumbnailError
|
||||||
|
from tagging.fields import TagField
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
from sorl.thumbnail import ImageField, get_thumbnail
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
User = get_user_model()
|
||||||
|
except ImportError:
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
try:
|
||||||
|
import Image as PILImage
|
||||||
|
except ImportError:
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
|
from imagestore.utils import get_file_path, get_model_string
|
||||||
|
|
||||||
|
SELF_MANAGE = getattr(settings, 'IMAGESTORE_SELF_MANAGE', True)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseImage(models.Model):
|
||||||
|
class Meta(object):
|
||||||
|
abstract = True
|
||||||
|
ordering = ('order', 'id')
|
||||||
|
permissions = (
|
||||||
|
('moderate_images', 'View, update and delete any image'),
|
||||||
|
)
|
||||||
|
|
||||||
|
title = models.CharField(_('Title'), max_length=100, blank=True, null=True)
|
||||||
|
description = models.TextField(_('Description'), blank=True, null=True)
|
||||||
|
tags = TagField(_('Tags'), blank=True)
|
||||||
|
order = models.IntegerField(_('Order'), default=0)
|
||||||
|
image = ImageField(verbose_name = _('File'), upload_to=get_file_path)
|
||||||
|
user = models.ForeignKey(User, verbose_name=_('User'), null=True, blank=True, related_name='images')
|
||||||
|
created = models.DateTimeField(_('Created'), auto_now_add=True, null=True)
|
||||||
|
updated = models.DateTimeField(_('Updated'), auto_now=True, null=True)
|
||||||
|
album = models.ForeignKey(get_model_string('Album'), verbose_name=_('Album'), null=True, blank=True, related_name='images')
|
||||||
|
|
||||||
|
@permalink
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return 'imagestore:image', (), {'pk': self.id}
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return '%s'% self.id
|
||||||
|
|
||||||
|
def admin_thumbnail(self):
|
||||||
|
try:
|
||||||
|
return '<img src="%s">' % get_thumbnail(self.image, '100x100', crop='center').url
|
||||||
|
except IOError:
|
||||||
|
return 'IOError'
|
||||||
|
except ThumbnailError, ex:
|
||||||
|
return 'ThumbnailError, %s' % ex.message
|
||||||
|
|
||||||
|
admin_thumbnail.short_description = _('Thumbnail')
|
||||||
|
admin_thumbnail.allow_tags = True
|
||||||
|
|
||||||
|
|
||||||
|
#noinspection PyUnusedLocal
|
||||||
|
def setup_imagestore_permissions(instance, created, **kwargs):
|
||||||
|
if not created:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
from imagestore.models import Album, Image
|
||||||
|
album_type = ContentType.objects.get(
|
||||||
|
#app_label=load_class('imagestore.models.Album')._meta.app_label,
|
||||||
|
app_label = Album._meta.app_label,
|
||||||
|
name='Album'
|
||||||
|
)
|
||||||
|
image_type = ContentType.objects.get(
|
||||||
|
#app_label=load_class('imagestore.models.Image')._meta.app_label,
|
||||||
|
app_label = Image._meta.app_label,
|
||||||
|
name='Image'
|
||||||
|
)
|
||||||
|
add_image_permission = Permission.objects.get(codename='add_image', content_type=image_type)
|
||||||
|
add_album_permission = Permission.objects.get(codename='add_album', content_type=album_type)
|
||||||
|
change_image_permission = Permission.objects.get(codename='change_image', content_type=image_type)
|
||||||
|
change_album_permission = Permission.objects.get(codename='change_album', content_type=album_type)
|
||||||
|
delete_image_permission = Permission.objects.get(codename='delete_image', content_type=image_type)
|
||||||
|
delete_album_permission = Permission.objects.get(codename='delete_album', content_type=album_type)
|
||||||
|
instance.user_permissions.add(add_image_permission, add_album_permission,)
|
||||||
|
instance.user_permissions.add(change_image_permission, change_album_permission,)
|
||||||
|
instance.user_permissions.add(delete_image_permission, delete_album_permission,)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
# Permissions are not yet installed or conten does not created yet
|
||||||
|
# probaly this is first
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if SELF_MANAGE:
|
||||||
|
post_save.connect(setup_imagestore_permissions, User)
|
||||||
15
imagestore/models/image.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
from bases.image import BaseImage
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from imagestore.utils import load_class, get_model_string
|
||||||
|
|
||||||
|
class Image(BaseImage):
|
||||||
|
class Meta(BaseImage.Meta):
|
||||||
|
abstract = False
|
||||||
|
verbose_name = _('Image')
|
||||||
|
verbose_name_plural = _('Images')
|
||||||
|
app_label = 'imagestore'
|
||||||
112
imagestore/models/upload.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
|
__author__ = 'zeus'
|
||||||
|
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
|
try:
|
||||||
|
import Image as PILImage
|
||||||
|
except ImportError:
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
|
from imagestore.models import Album, Image
|
||||||
|
|
||||||
|
TEMP_DIR = getattr(settings, 'TEMP_DIR', 'temp/')
|
||||||
|
|
||||||
|
|
||||||
|
def process_zipfile(uploaded_album):
|
||||||
|
if os.path.isfile(uploaded_album.zip_file.path):
|
||||||
|
# TODO: implement try-except here
|
||||||
|
zip = zipfile.ZipFile(uploaded_album.zip_file.path)
|
||||||
|
bad_file = zip.testzip()
|
||||||
|
if bad_file:
|
||||||
|
raise Exception('"%s" in the .zip archive is corrupt.' % bad_file)
|
||||||
|
|
||||||
|
if not uploaded_album.album:
|
||||||
|
uploaded_album.album = Album.objects.create(name=uploaded_album.new_album_name)
|
||||||
|
|
||||||
|
from cStringIO import StringIO
|
||||||
|
for filename in sorted(zip.namelist()):
|
||||||
|
if filename.startswith('__'): # do not process meta files
|
||||||
|
continue
|
||||||
|
print filename
|
||||||
|
data = zip.read(filename)
|
||||||
|
if len(data):
|
||||||
|
try:
|
||||||
|
# the following is taken from django.forms.fields.ImageField:
|
||||||
|
# load() could spot a truncated JPEG, but it loads the entire
|
||||||
|
# image in memory, which is a DoS vector. See #3848 and #18520.
|
||||||
|
# verify() must be called immediately after the constructor.
|
||||||
|
PILImage.open(StringIO(data)).verify()
|
||||||
|
except Exception, ex:
|
||||||
|
# if a "bad" file is found we just skip it.
|
||||||
|
print('Error verify image: %s' % ex.message)
|
||||||
|
continue
|
||||||
|
if hasattr(data, 'seek') and callable(data.seek):
|
||||||
|
print 'seeked'
|
||||||
|
data.seek(0)
|
||||||
|
try:
|
||||||
|
img = Image(album=uploaded_album.album)
|
||||||
|
img.image.save(filename, ContentFile(data))
|
||||||
|
img.save()
|
||||||
|
except Exception, ex:
|
||||||
|
print('error create Image: %s' % ex.message)
|
||||||
|
zip.close()
|
||||||
|
uploaded_album.delete()
|
||||||
|
|
||||||
|
|
||||||
|
upload_processor_function = getattr(settings, 'IMAGESTORE_UPLOAD_ALBUM_PROCESSOR', None)
|
||||||
|
upload_processor = process_zipfile
|
||||||
|
if upload_processor_function:
|
||||||
|
i = upload_processor_function.rfind('.')
|
||||||
|
module, attr = upload_processor_function[:i], upload_processor_function[i+1:]
|
||||||
|
try:
|
||||||
|
mod = import_module(module)
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e))
|
||||||
|
try:
|
||||||
|
upload_processor = getattr(mod, attr)
|
||||||
|
except AttributeError:
|
||||||
|
raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumUpload(models.Model):
|
||||||
|
"""
|
||||||
|
Just re-written django-photologue GalleryUpload method
|
||||||
|
"""
|
||||||
|
zip_file = models.FileField(_('images file (.zip)'), upload_to=TEMP_DIR,
|
||||||
|
help_text=_('Select a .zip file of images to upload into a new Gallery.'))
|
||||||
|
album = models.ForeignKey(
|
||||||
|
Album,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_('Select an album to add these images to. leave this empty to create a new album from the supplied title.')
|
||||||
|
)
|
||||||
|
new_album_name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_('New album name'),
|
||||||
|
help_text=_('If not empty new album with this name will be created and images will be upload to this album')
|
||||||
|
)
|
||||||
|
tags = models.CharField(max_length=255, blank=True, verbose_name=_('tags'))
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
verbose_name = _('Album upload')
|
||||||
|
verbose_name_plural = _('Album uploads')
|
||||||
|
app_label = 'imagestore'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super(AlbumUpload, self).save(*args, **kwargs)
|
||||||
|
upload_processor(self)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
storage, path = self.zip_file.storage, self.zip_file.path
|
||||||
|
super(AlbumUpload, self).delete(*args, **kwargs)
|
||||||
|
storage.delete(path)
|
||||||
107
imagestore/static/imagestore.css
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#category-list li{
|
||||||
|
font-size: 120%;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#category-list {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls .controls-group {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-description {
|
||||||
|
padding: 10px 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 0 10px 0;
|
||||||
|
font-size: 110%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation .next-link {
|
||||||
|
margin-left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.current {
|
||||||
|
border: 1px red solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-view img.preview {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-view {
|
||||||
|
margin-right: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-list .album {
|
||||||
|
width: 160px;
|
||||||
|
height: 200px;
|
||||||
|
float: left;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-name {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-user {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 80%;
|
||||||
|
color: #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-head {
|
||||||
|
height: 150px;
|
||||||
|
width: 150px;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.album-head a {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview {
|
||||||
|
float: left;
|
||||||
|
width: 130px;
|
||||||
|
padding: 5px;
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview .image-title {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-fields {
|
||||||
|
font-size: 80%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-fields label {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-fields, .extra-fields {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
30
imagestore/static/prettyphoto/README
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
prettyPhoto v3.1.4
|
||||||
|
© Copyright, Stephane Caron
|
||||||
|
http://www.no-margin-for-errors.com
|
||||||
|
|
||||||
|
|
||||||
|
============================= Released under =============================
|
||||||
|
|
||||||
|
Creative Commons 2.5
|
||||||
|
http://creativecommons.org/licenses/by/2.5/
|
||||||
|
|
||||||
|
OR
|
||||||
|
|
||||||
|
GPLV2 license
|
||||||
|
http://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
|
||||||
|
You are free to use prettyPhoto in commercial projects as long as the
|
||||||
|
copyright header is left intact.
|
||||||
|
|
||||||
|
============================ More information ============================
|
||||||
|
http://www.no-margin-for-errors.com/projects/prettyPhoto/
|
||||||
|
|
||||||
|
|
||||||
|
============================== Description ===============================
|
||||||
|
|
||||||
|
prettyPhoto is a jQuery based lightbox clone. Not only does it support images,
|
||||||
|
it also add support for videos, flash, YouTube, iFrame. It's a full blown
|
||||||
|
media modal box.
|
||||||
|
|
||||||
|
Please refer to http://www.no-margin-for-errors.com/projects/prettyPhoto/
|
||||||
|
for all the details on how to use.
|
||||||
170
imagestore/static/prettyphoto/css/prettyPhoto.css
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
div.pp_default .pp_top,div.pp_default .pp_top .pp_middle,div.pp_default .pp_top .pp_left,div.pp_default .pp_top .pp_right,div.pp_default .pp_bottom,div.pp_default .pp_bottom .pp_left,div.pp_default .pp_bottom .pp_middle,div.pp_default .pp_bottom .pp_right{height:13px}
|
||||||
|
div.pp_default .pp_top .pp_left{background:url(../images/prettyPhoto/default/sprite.png) -78px -93px no-repeat}
|
||||||
|
div.pp_default .pp_top .pp_middle{background:url(../images/prettyPhoto/default/sprite_x.png) top left repeat-x}
|
||||||
|
div.pp_default .pp_top .pp_right{background:url(../images/prettyPhoto/default/sprite.png) -112px -93px no-repeat}
|
||||||
|
div.pp_default .pp_content .ppt{color:#f8f8f8}
|
||||||
|
div.pp_default .pp_content_container .pp_left{background:url(../images/prettyPhoto/default/sprite_y.png) -7px 0 repeat-y;padding-left:13px}
|
||||||
|
div.pp_default .pp_content_container .pp_right{background:url(../images/prettyPhoto/default/sprite_y.png) top right repeat-y;padding-right:13px}
|
||||||
|
div.pp_default .pp_next:hover{background:url(../images/prettyPhoto/default/sprite_next.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.pp_default .pp_previous:hover{background:url(../images/prettyPhoto/default/sprite_prev.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.pp_default .pp_expand{background:url(../images/prettyPhoto/default/sprite.png) 0 -29px no-repeat;cursor:pointer;width:28px;height:28px}
|
||||||
|
div.pp_default .pp_expand:hover{background:url(../images/prettyPhoto/default/sprite.png) 0 -56px no-repeat;cursor:pointer}
|
||||||
|
div.pp_default .pp_contract{background:url(../images/prettyPhoto/default/sprite.png) 0 -84px no-repeat;cursor:pointer;width:28px;height:28px}
|
||||||
|
div.pp_default .pp_contract:hover{background:url(../images/prettyPhoto/default/sprite.png) 0 -113px no-repeat;cursor:pointer}
|
||||||
|
div.pp_default .pp_close{width:30px;height:30px;background:url(../images/prettyPhoto/default/sprite.png) 2px 1px no-repeat;cursor:pointer}
|
||||||
|
div.pp_default .pp_gallery ul li a{background:url(../images/prettyPhoto/default/default_thumb.png) center center #f8f8f8;border:1px solid #aaa}
|
||||||
|
div.pp_default .pp_social{margin-top:7px}
|
||||||
|
div.pp_default .pp_gallery a.pp_arrow_previous,div.pp_default .pp_gallery a.pp_arrow_next{position:static;left:auto}
|
||||||
|
div.pp_default .pp_nav .pp_play,div.pp_default .pp_nav .pp_pause{background:url(../images/prettyPhoto/default/sprite.png) -51px 1px no-repeat;height:30px;width:30px}
|
||||||
|
div.pp_default .pp_nav .pp_pause{background-position:-51px -29px}
|
||||||
|
div.pp_default a.pp_arrow_previous,div.pp_default a.pp_arrow_next{background:url(../images/prettyPhoto/default/sprite.png) -31px -3px no-repeat;height:20px;width:20px;margin:4px 0 0}
|
||||||
|
div.pp_default a.pp_arrow_next{left:52px;background-position:-82px -3px}
|
||||||
|
div.pp_default .pp_content_container .pp_details{margin-top:5px}
|
||||||
|
div.pp_default .pp_nav{clear:none;height:30px;width:110px;position:relative}
|
||||||
|
div.pp_default .pp_nav .currentTextHolder{font-family:Georgia;font-style:italic;color:#999;font-size:11px;left:75px;line-height:25px;position:absolute;top:2px;margin:0;padding:0 0 0 10px}
|
||||||
|
div.pp_default .pp_close:hover,div.pp_default .pp_nav .pp_play:hover,div.pp_default .pp_nav .pp_pause:hover,div.pp_default .pp_arrow_next:hover,div.pp_default .pp_arrow_previous:hover{opacity:0.7}
|
||||||
|
div.pp_default .pp_description{font-size:11px;font-weight:700;line-height:14px;margin:5px 50px 5px 0}
|
||||||
|
div.pp_default .pp_bottom .pp_left{background:url(../images/prettyPhoto/default/sprite.png) -78px -127px no-repeat}
|
||||||
|
div.pp_default .pp_bottom .pp_middle{background:url(../images/prettyPhoto/default/sprite_x.png) bottom left repeat-x}
|
||||||
|
div.pp_default .pp_bottom .pp_right{background:url(../images/prettyPhoto/default/sprite.png) -112px -127px no-repeat}
|
||||||
|
div.pp_default .pp_loaderIcon{background:url(../images/prettyPhoto/default/loader.gif) center center no-repeat}
|
||||||
|
div.light_rounded .pp_top .pp_left{background:url(../images/prettyPhoto/light_rounded/sprite.png) -88px -53px no-repeat}
|
||||||
|
div.light_rounded .pp_top .pp_right{background:url(../images/prettyPhoto/light_rounded/sprite.png) -110px -53px no-repeat}
|
||||||
|
div.light_rounded .pp_next:hover{background:url(../images/prettyPhoto/light_rounded/btnNext.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_previous:hover{background:url(../images/prettyPhoto/light_rounded/btnPrevious.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_expand{background:url(../images/prettyPhoto/light_rounded/sprite.png) -31px -26px no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_expand:hover{background:url(../images/prettyPhoto/light_rounded/sprite.png) -31px -47px no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_contract{background:url(../images/prettyPhoto/light_rounded/sprite.png) 0 -26px no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_contract:hover{background:url(../images/prettyPhoto/light_rounded/sprite.png) 0 -47px no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_close{width:75px;height:22px;background:url(../images/prettyPhoto/light_rounded/sprite.png) -1px -1px no-repeat;cursor:pointer}
|
||||||
|
div.light_rounded .pp_nav .pp_play{background:url(../images/prettyPhoto/light_rounded/sprite.png) -1px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.light_rounded .pp_nav .pp_pause{background:url(../images/prettyPhoto/light_rounded/sprite.png) -24px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.light_rounded .pp_arrow_previous{background:url(../images/prettyPhoto/light_rounded/sprite.png) 0 -71px no-repeat}
|
||||||
|
div.light_rounded .pp_arrow_next{background:url(../images/prettyPhoto/light_rounded/sprite.png) -22px -71px no-repeat}
|
||||||
|
div.light_rounded .pp_bottom .pp_left{background:url(../images/prettyPhoto/light_rounded/sprite.png) -88px -80px no-repeat}
|
||||||
|
div.light_rounded .pp_bottom .pp_right{background:url(../images/prettyPhoto/light_rounded/sprite.png) -110px -80px no-repeat}
|
||||||
|
div.dark_rounded .pp_top .pp_left{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -88px -53px no-repeat}
|
||||||
|
div.dark_rounded .pp_top .pp_right{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -110px -53px no-repeat}
|
||||||
|
div.dark_rounded .pp_content_container .pp_left{background:url(../images/prettyPhoto/dark_rounded/contentPattern.png) top left repeat-y}
|
||||||
|
div.dark_rounded .pp_content_container .pp_right{background:url(../images/prettyPhoto/dark_rounded/contentPattern.png) top right repeat-y}
|
||||||
|
div.dark_rounded .pp_next:hover{background:url(../images/prettyPhoto/dark_rounded/btnNext.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_previous:hover{background:url(../images/prettyPhoto/dark_rounded/btnPrevious.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_expand{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -31px -26px no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_expand:hover{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -31px -47px no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_contract{background:url(../images/prettyPhoto/dark_rounded/sprite.png) 0 -26px no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_contract:hover{background:url(../images/prettyPhoto/dark_rounded/sprite.png) 0 -47px no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_close{width:75px;height:22px;background:url(../images/prettyPhoto/dark_rounded/sprite.png) -1px -1px no-repeat;cursor:pointer}
|
||||||
|
div.dark_rounded .pp_description{margin-right:85px;color:#fff}
|
||||||
|
div.dark_rounded .pp_nav .pp_play{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -1px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.dark_rounded .pp_nav .pp_pause{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -24px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.dark_rounded .pp_arrow_previous{background:url(../images/prettyPhoto/dark_rounded/sprite.png) 0 -71px no-repeat}
|
||||||
|
div.dark_rounded .pp_arrow_next{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -22px -71px no-repeat}
|
||||||
|
div.dark_rounded .pp_bottom .pp_left{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -88px -80px no-repeat}
|
||||||
|
div.dark_rounded .pp_bottom .pp_right{background:url(../images/prettyPhoto/dark_rounded/sprite.png) -110px -80px no-repeat}
|
||||||
|
div.dark_rounded .pp_loaderIcon{background:url(../images/prettyPhoto/dark_rounded/loader.gif) center center no-repeat}
|
||||||
|
div.dark_square .pp_left,div.dark_square .pp_middle,div.dark_square .pp_right,div.dark_square .pp_content{background:#000}
|
||||||
|
div.dark_square .pp_description{color:#fff;margin:0 85px 0 0}
|
||||||
|
div.dark_square .pp_loaderIcon{background:url(../images/prettyPhoto/dark_square/loader.gif) center center no-repeat}
|
||||||
|
div.dark_square .pp_expand{background:url(../images/prettyPhoto/dark_square/sprite.png) -31px -26px no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_expand:hover{background:url(../images/prettyPhoto/dark_square/sprite.png) -31px -47px no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_contract{background:url(../images/prettyPhoto/dark_square/sprite.png) 0 -26px no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_contract:hover{background:url(../images/prettyPhoto/dark_square/sprite.png) 0 -47px no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_close{width:75px;height:22px;background:url(../images/prettyPhoto/dark_square/sprite.png) -1px -1px no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_nav{clear:none}
|
||||||
|
div.dark_square .pp_nav .pp_play{background:url(../images/prettyPhoto/dark_square/sprite.png) -1px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.dark_square .pp_nav .pp_pause{background:url(../images/prettyPhoto/dark_square/sprite.png) -24px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.dark_square .pp_arrow_previous{background:url(../images/prettyPhoto/dark_square/sprite.png) 0 -71px no-repeat}
|
||||||
|
div.dark_square .pp_arrow_next{background:url(../images/prettyPhoto/dark_square/sprite.png) -22px -71px no-repeat}
|
||||||
|
div.dark_square .pp_next:hover{background:url(../images/prettyPhoto/dark_square/btnNext.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.dark_square .pp_previous:hover{background:url(../images/prettyPhoto/dark_square/btnPrevious.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_expand{background:url(../images/prettyPhoto/light_square/sprite.png) -31px -26px no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_expand:hover{background:url(../images/prettyPhoto/light_square/sprite.png) -31px -47px no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_contract{background:url(../images/prettyPhoto/light_square/sprite.png) 0 -26px no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_contract:hover{background:url(../images/prettyPhoto/light_square/sprite.png) 0 -47px no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_close{width:75px;height:22px;background:url(../images/prettyPhoto/light_square/sprite.png) -1px -1px no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_nav .pp_play{background:url(../images/prettyPhoto/light_square/sprite.png) -1px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.light_square .pp_nav .pp_pause{background:url(../images/prettyPhoto/light_square/sprite.png) -24px -100px no-repeat;height:15px;width:14px}
|
||||||
|
div.light_square .pp_arrow_previous{background:url(../images/prettyPhoto/light_square/sprite.png) 0 -71px no-repeat}
|
||||||
|
div.light_square .pp_arrow_next{background:url(../images/prettyPhoto/light_square/sprite.png) -22px -71px no-repeat}
|
||||||
|
div.light_square .pp_next:hover{background:url(../images/prettyPhoto/light_square/btnNext.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.light_square .pp_previous:hover{background:url(../images/prettyPhoto/light_square/btnPrevious.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_top .pp_left{background:url(../images/prettyPhoto/facebook/sprite.png) -88px -53px no-repeat}
|
||||||
|
div.facebook .pp_top .pp_middle{background:url(../images/prettyPhoto/facebook/contentPatternTop.png) top left repeat-x}
|
||||||
|
div.facebook .pp_top .pp_right{background:url(../images/prettyPhoto/facebook/sprite.png) -110px -53px no-repeat}
|
||||||
|
div.facebook .pp_content_container .pp_left{background:url(../images/prettyPhoto/facebook/contentPatternLeft.png) top left repeat-y}
|
||||||
|
div.facebook .pp_content_container .pp_right{background:url(../images/prettyPhoto/facebook/contentPatternRight.png) top right repeat-y}
|
||||||
|
div.facebook .pp_expand{background:url(../images/prettyPhoto/facebook/sprite.png) -31px -26px no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_expand:hover{background:url(../images/prettyPhoto/facebook/sprite.png) -31px -47px no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_contract{background:url(../images/prettyPhoto/facebook/sprite.png) 0 -26px no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_contract:hover{background:url(../images/prettyPhoto/facebook/sprite.png) 0 -47px no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_close{width:22px;height:22px;background:url(../images/prettyPhoto/facebook/sprite.png) -1px -1px no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_description{margin:0 37px 0 0}
|
||||||
|
div.facebook .pp_loaderIcon{background:url(../images/prettyPhoto/facebook/loader.gif) center center no-repeat}
|
||||||
|
div.facebook .pp_arrow_previous{background:url(../images/prettyPhoto/facebook/sprite.png) 0 -71px no-repeat;height:22px;margin-top:0;width:22px}
|
||||||
|
div.facebook .pp_arrow_previous.disabled{background-position:0 -96px;cursor:default}
|
||||||
|
div.facebook .pp_arrow_next{background:url(../images/prettyPhoto/facebook/sprite.png) -32px -71px no-repeat;height:22px;margin-top:0;width:22px}
|
||||||
|
div.facebook .pp_arrow_next.disabled{background-position:-32px -96px;cursor:default}
|
||||||
|
div.facebook .pp_nav{margin-top:0}
|
||||||
|
div.facebook .pp_nav p{font-size:15px;padding:0 3px 0 4px}
|
||||||
|
div.facebook .pp_nav .pp_play{background:url(../images/prettyPhoto/facebook/sprite.png) -1px -123px no-repeat;height:22px;width:22px}
|
||||||
|
div.facebook .pp_nav .pp_pause{background:url(../images/prettyPhoto/facebook/sprite.png) -32px -123px no-repeat;height:22px;width:22px}
|
||||||
|
div.facebook .pp_next:hover{background:url(../images/prettyPhoto/facebook/btnNext.png) center right no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_previous:hover{background:url(../images/prettyPhoto/facebook/btnPrevious.png) center left no-repeat;cursor:pointer}
|
||||||
|
div.facebook .pp_bottom .pp_left{background:url(../images/prettyPhoto/facebook/sprite.png) -88px -80px no-repeat}
|
||||||
|
div.facebook .pp_bottom .pp_middle{background:url(../images/prettyPhoto/facebook/contentPatternBottom.png) top left repeat-x}
|
||||||
|
div.facebook .pp_bottom .pp_right{background:url(../images/prettyPhoto/facebook/sprite.png) -110px -80px no-repeat}
|
||||||
|
div.pp_pic_holder a:focus{outline:none}
|
||||||
|
div.pp_overlay{background:#000;display:none;left:0;position:absolute;top:0;width:100%;z-index:9500}
|
||||||
|
div.pp_pic_holder{display:none;position:absolute;width:100px;z-index:10000}
|
||||||
|
.pp_content{height:40px;min-width:40px}
|
||||||
|
* html .pp_content{width:40px}
|
||||||
|
.pp_content_container{position:relative;text-align:left;width:100%}
|
||||||
|
.pp_content_container .pp_left{padding-left:20px}
|
||||||
|
.pp_content_container .pp_right{padding-right:20px}
|
||||||
|
.pp_content_container .pp_details{float:left;margin:10px 0 2px}
|
||||||
|
.pp_description{display:none;margin:0}
|
||||||
|
.pp_social{float:left;margin:0}
|
||||||
|
.pp_social .facebook{float:left;margin-left:5px;width:55px;overflow:hidden}
|
||||||
|
.pp_social .twitter{float:left}
|
||||||
|
.pp_nav{clear:right;float:left;margin:3px 10px 0 0}
|
||||||
|
.pp_nav p{float:left;white-space:nowrap;margin:2px 4px}
|
||||||
|
.pp_nav .pp_play,.pp_nav .pp_pause{float:left;margin-right:4px;text-indent:-10000px}
|
||||||
|
a.pp_arrow_previous,a.pp_arrow_next{display:block;float:left;height:15px;margin-top:3px;overflow:hidden;text-indent:-10000px;width:14px}
|
||||||
|
.pp_hoverContainer{position:absolute;top:0;width:100%;z-index:2000}
|
||||||
|
.pp_gallery{display:none;left:50%;margin-top:-50px;position:absolute;z-index:10000}
|
||||||
|
.pp_gallery div{float:left;overflow:hidden;position:relative}
|
||||||
|
.pp_gallery ul{float:left;height:35px;position:relative;white-space:nowrap;margin:0 0 0 5px;padding:0}
|
||||||
|
.pp_gallery ul a{border:1px rgba(0,0,0,0.5) solid;display:block;float:left;height:33px;overflow:hidden}
|
||||||
|
.pp_gallery ul a img{border:0}
|
||||||
|
.pp_gallery li{display:block;float:left;margin:0 5px 0 0;padding:0}
|
||||||
|
.pp_gallery li.default a{background:url(../images/prettyPhoto/facebook/default_thumbnail.gif) 0 0 no-repeat;display:block;height:33px;width:50px}
|
||||||
|
.pp_gallery .pp_arrow_previous,.pp_gallery .pp_arrow_next{margin-top:7px!important}
|
||||||
|
a.pp_next{background:url(../images/prettyPhoto/light_rounded/btnNext.png) 10000px 10000px no-repeat;display:block;float:right;height:100%;text-indent:-10000px;width:49%}
|
||||||
|
a.pp_previous{background:url(../images/prettyPhoto/light_rounded/btnNext.png) 10000px 10000px no-repeat;display:block;float:left;height:100%;text-indent:-10000px;width:49%}
|
||||||
|
a.pp_expand,a.pp_contract{cursor:pointer;display:none;height:20px;position:absolute;right:30px;text-indent:-10000px;top:10px;width:20px;z-index:20000}
|
||||||
|
a.pp_close{position:absolute;right:0;top:0;display:block;line-height:22px;text-indent:-10000px}
|
||||||
|
.pp_loaderIcon{display:block;height:24px;left:50%;position:absolute;top:50%;width:24px;margin:-12px 0 0 -12px}
|
||||||
|
#pp_full_res{line-height:1!important}
|
||||||
|
#pp_full_res .pp_inline{text-align:left}
|
||||||
|
#pp_full_res .pp_inline p{margin:0 0 15px}
|
||||||
|
div.ppt{color:#fff;display:none;font-size:17px;z-index:9999;margin:0 0 5px 15px}
|
||||||
|
div.pp_default .pp_content,div.light_rounded .pp_content{background-color:#fff}
|
||||||
|
div.pp_default #pp_full_res .pp_inline,div.light_rounded .pp_content .ppt,div.light_rounded #pp_full_res .pp_inline,div.light_square .pp_content .ppt,div.light_square #pp_full_res .pp_inline,div.facebook .pp_content .ppt,div.facebook #pp_full_res .pp_inline{color:#000}
|
||||||
|
div.pp_default .pp_gallery ul li a:hover,div.pp_default .pp_gallery ul li.selected a,.pp_gallery ul a:hover,.pp_gallery li.selected a{border-color:#fff}
|
||||||
|
div.pp_default .pp_details,div.light_rounded .pp_details,div.dark_rounded .pp_details,div.dark_square .pp_details,div.light_square .pp_details,div.facebook .pp_details{position:relative}
|
||||||
|
div.light_rounded .pp_top .pp_middle,div.light_rounded .pp_content_container .pp_left,div.light_rounded .pp_content_container .pp_right,div.light_rounded .pp_bottom .pp_middle,div.light_square .pp_left,div.light_square .pp_middle,div.light_square .pp_right,div.light_square .pp_content,div.facebook .pp_content{background:#fff}
|
||||||
|
div.light_rounded .pp_description,div.light_square .pp_description{margin-right:85px}
|
||||||
|
div.light_rounded .pp_gallery a.pp_arrow_previous,div.light_rounded .pp_gallery a.pp_arrow_next,div.dark_rounded .pp_gallery a.pp_arrow_previous,div.dark_rounded .pp_gallery a.pp_arrow_next,div.dark_square .pp_gallery a.pp_arrow_previous,div.dark_square .pp_gallery a.pp_arrow_next,div.light_square .pp_gallery a.pp_arrow_previous,div.light_square .pp_gallery a.pp_arrow_next{margin-top:12px!important}
|
||||||
|
div.light_rounded .pp_arrow_previous.disabled,div.dark_rounded .pp_arrow_previous.disabled,div.dark_square .pp_arrow_previous.disabled,div.light_square .pp_arrow_previous.disabled{background-position:0 -87px;cursor:default}
|
||||||
|
div.light_rounded .pp_arrow_next.disabled,div.dark_rounded .pp_arrow_next.disabled,div.dark_square .pp_arrow_next.disabled,div.light_square .pp_arrow_next.disabled{background-position:-22px -87px;cursor:default}
|
||||||
|
div.light_rounded .pp_loaderIcon,div.light_square .pp_loaderIcon{background:url(../images/prettyPhoto/light_rounded/loader.gif) center center no-repeat}
|
||||||
|
div.dark_rounded .pp_top .pp_middle,div.dark_rounded .pp_content,div.dark_rounded .pp_bottom .pp_middle{background:url(../images/prettyPhoto/dark_rounded/contentPattern.png) top left repeat}
|
||||||
|
div.dark_rounded .currentTextHolder,div.dark_square .currentTextHolder{color:#c4c4c4}
|
||||||
|
div.dark_rounded #pp_full_res .pp_inline,div.dark_square #pp_full_res .pp_inline{color:#fff}
|
||||||
|
.pp_top,.pp_bottom{height:20px;position:relative}
|
||||||
|
* html .pp_top,* html .pp_bottom{padding:0 20px}
|
||||||
|
.pp_top .pp_left,.pp_bottom .pp_left{height:20px;left:0;position:absolute;width:20px}
|
||||||
|
.pp_top .pp_middle,.pp_bottom .pp_middle{height:20px;left:20px;position:absolute;right:20px}
|
||||||
|
* html .pp_top .pp_middle,* html .pp_bottom .pp_middle{left:0;position:static}
|
||||||
|
.pp_top .pp_right,.pp_bottom .pp_right{height:20px;left:auto;position:absolute;right:0;top:0;width:20px}
|
||||||
|
.pp_fade,.pp_gallery li.default a img{display:none}
|
||||||
BIN
imagestore/static/prettyphoto/images/.DS_Store
vendored
Normal file
BIN
imagestore/static/prettyphoto/images/fullscreen/1.jpg
Executable file
|
After Width: | Height: | Size: 60 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/2.jpg
Executable file
|
After Width: | Height: | Size: 82 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/3.jpg
Executable file
|
After Width: | Height: | Size: 35 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/4.jpg
Executable file
|
After Width: | Height: | Size: 96 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/5.jpg
Executable file
|
After Width: | Height: | Size: 43 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/6.jpg
Executable file
|
After Width: | Height: | Size: 940 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/high.gif
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/huge.gif
Executable file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
imagestore/static/prettyphoto/images/fullscreen/wide.gif
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
imagestore/static/prettyphoto/images/prettyPhoto/dark_rounded/btnNext.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |