From: Guillaume Pellerin Date: Thu, 7 Jul 2016 17:02:03 +0000 (+0200) Subject: merge X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=150cb7f151b3b756257b3b6d7371b42a43c4921f;p=mezzo.git merge --- diff --git a/.gitignore b/.gitignore index af68934c..ce8bf438 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ develop-eggs *.directory var data/static +data/mysql +data/postgresql .thumbnails # Installer logs diff --git a/app/local_settings.py b/app/local_settings.py index 5b6f5d61..96e92ada 100644 --- a/app/local_settings.py +++ b/app/local_settings.py @@ -12,12 +12,12 @@ NEVERCACHE_KEY = "m)u^%r@uh#r3wu0&$=#$1ogx)uy4hv93^2lt%c3@xi=^gifoj8paozijdihaze DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'USER': os.environ.get('DB_ENV_MYSQL_USER'), # Not used with sqlite3. - 'PASSWORD': os.environ.get('DB_ENV_MYSQL_PASSWORD'), # Not used with sqlite3. - 'NAME': os.environ.get('DB_ENV_MYSQL_DATABASE'), - 'HOST': 'db', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '3306', # Set to empty string for default. Not used with sqlite3. + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'postgres', + 'USER': 'postgres', + 'PASSWORD': os.environ.get('DB_ENV_POSTGRES_PASSWORD'), + 'HOST': 'db', + 'PORT': '5432', }, 'eve': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', @@ -27,15 +27,6 @@ DATABASES = { 'HOST': 'pgdb', 'PORT': '5432', }, - #'eve': { - # 'ENGINE': 'django.db.backends.postgresql_psycopg2', - # 'NAME': 'eve', - # 'USER': 'django', - # 'PASSWORD': 'q2nqzt0WGnwWé,256', - # 'HOST': 'eve.ircam.fr', - # 'PORT': '5432', - #}, - } # DATABASE_ROUTERS = ['eve.routers.EveRouter',] @@ -69,24 +60,29 @@ FILEBROWSER_SELECT_FORMATS = { EMAIL_HOST = 'smtp.ircam.fr' EMAIL_PORT = '25' -DEFAULT_FROM_EMAIL = 'manifeste2016@ircam.fr' -EMAIL_SUBJECT_PREFIX = "IRCAM Manifeste 2016" +DEFAULT_FROM_EMAIL = 'www@ircam.fr' +EMAIL_SUBJECT_PREFIX = "[IRCAM WWW]" -SITE_TITLE = 'Manifeste 2016' -SITE_TAGLINE = 'Festival 2 juin | 2 juillet 2016' +SITE_TITLE = 'IRCAM' +SITE_TAGLINE = 'Institut de Recherche et de Coordination Acoustique et Musique' SILENCED_SYSTEM_CHECKS = ['fields.W342',] ADMIN_MENU_ORDER = ( - (_("Content"), ("pages.Page", "blog.BlogPost", "mezzanine_agenda.Event", - "festival.Artist", "festival.Video", "festival.Audio", "festival.Playlist", - "festival.Featured", - "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)), - (_("Site"), ("sites.Site", "redirects.Redirect", "conf.Setting")), - (_("Users"), ("auth.User", "auth.Group",)), - (_("Festival"), ("mezzanine_agenda.EventLocation", - "mezzanine_agenda.EventCategory", "mezzanine_agenda.EventPrice", - "festival.PageCategory",)), + (_('Content'), ('pages.Page', 'blog.BlogPost', 'mezzanine_agenda.Event', + 'generic.ThreadedComment', (_('Media Library'), 'fb_browse'),)), + (_("Magazine"), ("magazine.Article",)), + (_('team'), ('organization.team.Organization', 'organization.team.Team', + 'organization.team.Department', 'organization.team.Person', + 'organization.team.Activity')), + (_('Projects'), ('organization.project.Project')), + (_('Festival'), ('organization.festival.Artist', 'organization.festival.Video', + 'organization.festival.Audio', 'organization.festival.Playlist', + 'organization.festival.Featured', 'mezzanine_agenda.EventLocation', + 'mezzanine_agenda.EventCategory', 'mezzanine_agenda.EventPrice', + 'festival.PageCategory',)), + (_('Users'), ('auth.User', 'auth.Group',)), + (_('Site'), ('sites.Site', 'redirects.Redirect', 'conf.Setting')), ) GRAPPELLI_ADMIN_TITLE = 'IRCAM Admin' @@ -116,6 +112,10 @@ BREAKING_NEWS_FEATURED_ID = 4 BLOG_POST_PER_PAGE = 200 +# The numeric mode to set newly-uploaded files to. The value should be +# a mode you'd pass directly to os.chmod. +FILE_UPLOAD_PERMISSIONS = 0o664 +FILE_UPLOAD_TEMP_DIR = '/srv/media/uploads/tmp/' FILEBROWSER_MAX_UPLOAD_SIZE = 512000000 if DEBUG: @@ -137,3 +137,6 @@ DEBUG_TOOLBAR_PANELS = [ 'debug_toolbar.panels.logging.LoggingPanel', 'debug_toolbar.panels.redirects.RedirectsPanel', ] + +# slug +BLOG_SLUG = 'article' diff --git a/app/organization/__init__.py b/app/organization/__init__.py index e69de29b..efe892af 100644 --- a/app/organization/__init__.py +++ b/app/organization/__init__.py @@ -0,0 +1,2 @@ + +__version__ = 0.1 diff --git a/app/organization/core/__init__.py b/app/organization/core/__init__.py new file mode 100644 index 00000000..141c2fb0 --- /dev/null +++ b/app/organization/core/__init__.py @@ -0,0 +1,7 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +default_app_config = 'organization.core.apps.CoreConfig' diff --git a/app/organization/core/admin.py b/app/organization/core/admin.py new file mode 100644 index 00000000..14b200a0 --- /dev/null +++ b/app/organization/core/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from copy import deepcopy +from mezzanine.pages.models import Page +from mezzanine.pages.admin import PageAdmin +from mezzanine.pages.models import RichTextPage +from organization.core.models import BasicPage + + +admin.site.register(BasicPage, PageAdmin) diff --git a/app/organization/core/apps.py b/app/organization/core/apps.py new file mode 100644 index 00000000..b05d77a9 --- /dev/null +++ b/app/organization/core/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + +from django.core.checks import register + + +class CoreConfig(AppConfig): + + name = 'organization.core' + label = 'organization core' diff --git a/app/organization/core/management/__init__.py b/app/organization/core/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/core/management/commands/__init__.py b/app/organization/core/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/core/management/commands/create-admin-user.py b/app/organization/core/management/commands/create-admin-user.py new file mode 100644 index 00000000..e7e3cad5 --- /dev/null +++ b/app/organization/core/management/commands/create-admin-user.py @@ -0,0 +1,24 @@ +from optparse import make_option +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User + + +class Command(BaseCommand): + help = """Create a default admin user if it doesn't exist. + you SHOULD change the password and the email afterwards!""" + + username = 'admin' + password = 'admin' + email = 'root@example.com' + + def handle(self, *args, **options): + admin = User.objects.filter(username=self.username) + if not admin: + user = User(username=self.username) + user.set_password(self.password) + user.email = self.email + user.is_superuser = True + user.is_staff = True + user.save() + print('User ' + self.username + ' created') diff --git a/app/organization/core/management/commands/festival-sync-eve-events.py b/app/organization/core/management/commands/festival-sync-eve-events.py new file mode 100644 index 00000000..070cadbd --- /dev/null +++ b/app/organization/core/management/commands/festival-sync-eve-events.py @@ -0,0 +1,86 @@ +from datetime import datetime, timedelta +from optparse import make_option + +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User +from django.core.mail import EmailMessage + +import mezzanine_agenda.models as ma_models +from mezzanine.generic.models import AssignedKeyword, Keyword + +import eve.models as eve_models + + +class Command(BaseCommand): + """Synchronize events from E-vement to mezzanine_agenda""" + + + option_list = BaseCommand.option_list + ( + make_option('-m', '--meta_event', + dest='meta_event', + help='define eve meta_event'), + ) + + default_user = User.objects.get(username='admin') + + def cleanup(self): + # for event in ma_models.Event.objects.all(): + # event.delete() + # for location in ma_models.EventLocation.objects.all(): + # location.delete() + for event_price in ma_models.EventPrice.objects.all(): + event_price.delete() + + def handle(self, *args, **kwargs): + # self.cleanup() + meta_event_name = kwargs.get('meta_event') + meta_trans_all = eve_models.MetaEventTranslation.objects.all() + for meta_trans in meta_trans_all: + if meta_trans.name == meta_event_name: + break + eve_events = eve_models.Event.objects.filter(meta_event=meta_trans.id) + for eve_event in eve_events: + event_trans = eve_models.EventTranslation.objects.filter(id=eve_event, lang='fr')[0] + manifestations = eve_event.manifestations.all().order_by('happens_at') + first = True + for manifestation in manifestations: + events = ma_models.Event.objects.filter(external_id=manifestation.id) + if not events: + event = ma_models.Event(external_id=manifestation.id) + else: + event = events[0] + event.start = manifestation.happens_at + event.end = manifestation.happens_at + timedelta(seconds=manifestation.duration) + event.title = event_trans.name + event.user = self.default_user + + locations = ma_models.EventLocation.objects.filter(title=manifestation.location.name) + if locations: + location = locations[0] + else: + location = ma_models.EventLocation(title=manifestation.location.name) + address = '\n'.join([manifestation.location.address, manifestation.location.postalcode + ' ' + manifestation.location.city]) + location.address = address + location.external_id = manifestation.id + location.clean() + location.save() + event.location = location + event.save() + keyword, _ = Keyword.objects.get_or_create(title=eve_event.event_category.name) + event.keywords.add(AssignedKeyword(keyword=keyword), bulk=False) + + eve_prices = eve_models.PriceManifestation.objects.filter(manifestation=manifestation) + for price in eve_prices: + event_price, c = ma_models.EventPrice.objects.get_or_create(value=float(price.value)) + if event: + if not event_price in event.prices.all(): + event.prices.add(event_price) + + if not first: + event.parent = parent + else: + parent = event + first = False + + event.save() diff --git a/app/organization/core/management/commands/wait-for-db.py b/app/organization/core/management/commands/wait-for-db.py new file mode 100644 index 00000000..e2bacf00 --- /dev/null +++ b/app/organization/core/management/commands/wait-for-db.py @@ -0,0 +1,30 @@ +import os, time + +from optparse import make_option +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError +from django.db import connections + + +class Command(BaseCommand): + help = "wait for default DB connection" + + db_name = 'default' + N = 20 + + def handle(self, *args, **options): + i = 0 + connected = False + db_conn = connections[self.db_name] + while not connected: + try: + c = db_conn.cursor() + connected = True + except: + print('error connecting to DB...') + if i > self.N: + print('...exiting') + raise + print('...retrying') + i += 1 + time.sleep(1) diff --git a/app/organization/core/migrations/0001_initial.py b/app/organization/core/migrations/0001_initial.py new file mode 100644 index 00000000..9f094560 --- /dev/null +++ b/app/organization/core/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-06 16:48 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('pages', '0004_auto_20151223_1313'), + ] + + operations = [ + migrations.CreateModel( + name='BasicPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pages.Page')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('photo', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='photo')), + ('photo_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo credits')), + ('photo_alignment', models.CharField(blank=True, choices=[('left', 'left'), ('center', 'center'), ('right', 'right')], default='left', max_length=32, verbose_name='photo alignment')), + ('photo_description', models.TextField(blank=True, verbose_name='photo description')), + ('photo_card', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='card photo')), + ('photo_card_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo card credits')), + ('photo_slider', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='slider photo')), + ('photo_slider_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo slider credits')), + ('sub_title', models.TextField(blank=True, max_length=1024, verbose_name='sub title')), + ('sub_title_fr', models.TextField(blank=True, max_length=1024, null=True, verbose_name='sub title')), + ('sub_title_en', models.TextField(blank=True, max_length=1024, null=True, verbose_name='sub title')), + ], + options={ + 'verbose_name': 'basic page', + 'ordering': ('_order',), + }, + bases=('pages.page', models.Model), + ), + ] diff --git a/app/organization/core/migrations/0002_auto_20160707_1614.py b/app/organization/core/migrations/0002_auto_20160707_1614.py new file mode 100644 index 00000000..5cebb5ff --- /dev/null +++ b/app/organization/core/migrations/0002_auto_20160707_1614.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization core', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='basicpage', + name='photo', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_alignment', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_card', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_card_credits', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_credits', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_description', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_slider', + ), + migrations.RemoveField( + model_name='basicpage', + name='photo_slider_credits', + ), + ] diff --git a/app/organization/core/migrations/__init__.py b/app/organization/core/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/core/models.py b/app/organization/core/models.py new file mode 100644 index 00000000..c0d3ae04 --- /dev/null +++ b/app/organization/core/models.py @@ -0,0 +1,51 @@ +from django.db import models +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, reverse_lazy + +from mezzanine.pages.models import Page, RichText +from mezzanine.core.fields import RichTextField, OrderField, FileField + + +class Named(models.Model): + """Named object with description""" + + name = models.CharField(_('name'), max_length=512) + description = models.TextField(_('description'), blank=True) + + class Meta: + abstract = True + + def __unicode__(self): + return self.name + + @property + def slug(self): + return slugify(self.__unicode__()) + + +class Titled(models.Model): + """Base object with title and description""" + + title = models.CharField(_('title'), max_length=512) + description = models.TextField(_('description'), blank=True) + + class Meta: + abstract = True + + def __unicode__(self): + return self.title + + +class SubTitle(models.Model): + + sub_title = models.TextField(_('sub title'), blank=True, max_length=1024) + + class Meta: + abstract = True + + +class BasicPage(Page, RichText, SubTitle): + + class Meta: + verbose_name = 'basic page' diff --git a/app/organization/core/related.py b/app/organization/core/related.py new file mode 100644 index 00000000..5b1affbd --- /dev/null +++ b/app/organization/core/related.py @@ -0,0 +1,32 @@ +from django.core import exceptions +from django.db.models.fields.related import ForeignKey +from django.db.utils import ConnectionHandler, ConnectionRouter + +connections = ConnectionHandler() +router = ConnectionRouter() + + +class SpanningForeignKey(ForeignKey): + + def validate(self, value, model_instance): + if self.rel.parent_link: + return + # Call the grandparent rather than the parent to skip validation + super(ForeignKey, self).validate(value, model_instance) + if value is None: + return + + using = router.db_for_read(self.rel.to, instance=model_instance) + qs = self.rel.to._default_manager.using(using).filter( + **{self.rel.field_name: value} + ) + qs = qs.complex_filter(self.get_limit_choices_to()) + if not qs.exists(): + raise exceptions.ValidationError( + self.error_messages['invalid'], + code='invalid', + params={ + 'model': self.rel.to._meta.verbose_name, 'pk': value, + 'field': self.rel.field_name, 'value': value, + }, # 'pk' is included for backwards compatibility + ) diff --git a/app/organization/core/templatetags/organization_tags.py b/app/organization/core/templatetags/organization_tags.py new file mode 100644 index 00000000..5f3c9583 --- /dev/null +++ b/app/organization/core/templatetags/organization_tags.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from mezzanine.pages.models import Page +from mezzanine.blog.models import BlogPost +from mezzanine.template import Library +from mezzanine_agenda.models import Event +from mezzanine.conf import settings +from random import shuffle + +from organization.festival.models import * +from organization.magazine.models import * + +register = Library() + + +@register.filter +def subtract(value, arg): + return value - arg + +@register.as_tag +def featured_edito(*args): + qs = Page.objects.filter(slug="edito") + if qs: + return qs[0].get_content_model() + else: + return None + +@register.as_tag +def featured_events(*args): + featured = Featured.objects.all() + if featured: + return featured[0].events.order_by('start') + return None + +@register.as_tag +def featured(*args): + featured_list = [] + featured = Featured.objects.filter(id=settings.HOME_FEATURED_ID) + if featured: + featured = featured[0] + for post in featured.blogposts.all(): + featured_list.append(post) + for video in featured.videos.all(): + featured_list.append(video) + for artist in featured.artists.all(): + featured_list.append(artist) + for playlist in featured.playlists.all(): + featured_list.append(playlist) + shuffle(featured_list) + return featured_list + +@register.as_tag +def featured_breaking_news_content(*args): + featured = Featured.objects.filter(id=settings.BREAKING_NEWS_FEATURED_ID) + if featured: + featured = featured[0] + news = featured.pages.all() + if news: + return news[0].richtextpage.content + else: + return '' + return '' + +@register.filter +def get_class(obj): + return obj.__class__.__name__ + +@register.filter +def unique_posts(events): + post_list = [] + for event in events: + for post in event.blog_posts.all(): + print(post) + if not post in post_list: + post_list.append(post) + return post_list + +@register.filter +def no_parents(events): + return events.filter(parent=None) diff --git a/app/organization/core/tests.py b/app/organization/core/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/core/translation.py b/app/organization/core/translation.py new file mode 100644 index 00000000..571ac7ca --- /dev/null +++ b/app/organization/core/translation.py @@ -0,0 +1,15 @@ +from modeltranslation.translator import translator, register, TranslationOptions +from mezzanine.pages.models import Page, RichText +from mezzanine.pages.translation import TranslatedRichText + +from organization.core.models import BasicPage + +# @register(SubTitle) +# class SubTitleTranslationOptions(TranslationOptions): +# +# fields = ('sub_title',) + +@register(BasicPage) +class BasicPageTranslationOptions(TranslationOptions): + + fields = ('sub_title',) diff --git a/app/organization/core/urls.py b/app/organization/core/urls.py new file mode 100644 index 00000000..68af3c40 --- /dev/null +++ b/app/organization/core/urls.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + +from organization.core.views import HomeView + + +urlpatterns = [ + url("^$", HomeView.as_view(), name="home"), +] diff --git a/app/organization/core/views.py b/app/organization/core/views.py new file mode 100644 index 00000000..82dc1b0c --- /dev/null +++ b/app/organization/core/views.py @@ -0,0 +1,20 @@ +from django.shortcuts import render, get_object_or_404 +from django.http import Http404 +from django.views.generic.base import View +from django.views.generic import DetailView, ListView, TemplateView + + +class SlugMixin(object): + + def get_object(self): + objects = self.model.objects.all() + return get_object_or_404(objects, slug=self.kwargs['slug']) + + +class HomeView(TemplateView): + + template_name = 'index.html' + + def get_context_data(self, **kwargs): + context = super(HomeView, self).get_context_data(**kwargs) + return context diff --git a/app/organization/featured/__init__.py b/app/organization/featured/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/featured/admin.py b/app/organization/featured/admin.py new file mode 100644 index 00000000..1318a91e --- /dev/null +++ b/app/organization/featured/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from organization.featured.models import * + + +class FeaturedAdmin(admin.ModelAdmin): + + model = Featured + list_display = ('__unicode__',) + filter_horizontal = ['events', 'videos', 'articles', 'pages', 'playlists'] + + +admin.site.register(Featured, FeaturedAdmin) diff --git a/app/organization/featured/apps.py b/app/organization/featured/apps.py new file mode 100644 index 00000000..10cae19d --- /dev/null +++ b/app/organization/featured/apps.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class FeaturedConfig(AppConfig): + name = 'featured' diff --git a/app/organization/featured/migrations/0001_initial.py b/app/organization/featured/migrations/0001_initial.py new file mode 100644 index 00000000..00bd148a --- /dev/null +++ b/app/organization/featured/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('organization magazine', '0002_article'), + ('mezzanine_agenda', '0014_event_brochure'), + ('organization core', '0002_auto_20160707_1614'), + ] + + operations = [ + migrations.CreateModel( + name='Featured', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('articles', models.ManyToManyField(blank=True, related_name='featured', to='organization magazine.Article', verbose_name='articles')), + ('events', models.ManyToManyField(blank=True, related_name='featured', to='mezzanine_agenda.Event', verbose_name='events')), + ('pages', models.ManyToManyField(blank=True, related_name='featured', to='organization core.BasicPage', verbose_name='pages')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/app/organization/featured/migrations/0002_auto_20160707_1614.py b/app/organization/featured/migrations/0002_auto_20160707_1614.py new file mode 100644 index 00000000..2816f5c3 --- /dev/null +++ b/app/organization/featured/migrations/0002_auto_20160707_1614.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('featured', '0001_initial'), + ('organization media', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='featured', + name='playlists', + field=models.ManyToManyField(blank=True, related_name='featured', to='organization media.Playlist', verbose_name='playlists'), + ), + migrations.AddField( + model_name='featured', + name='videos', + field=models.ManyToManyField(blank=True, related_name='featured', to='organization media.Video', verbose_name='videos'), + ), + ] diff --git a/app/organization/featured/migrations/__init__.py b/app/organization/featured/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/featured/models.py b/app/organization/featured/models.py new file mode 100644 index 00000000..ae59ea49 --- /dev/null +++ b/app/organization/featured/models.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, reverse_lazy +from django.conf import settings + +from organization.core.models import * +from organization.magazine.models import * +from organization.media.models import * + +from mezzanine_agenda.models import Event + + +class Featured(Named): + """(Featured description)""" + + pages = models.ManyToManyField(BasicPage, verbose_name=_('pages'), related_name='featured', blank=True) + articles = models.ManyToManyField(Article, verbose_name=_('articles'), related_name='featured', blank=True) + events = models.ManyToManyField(Event, verbose_name=_('events'), related_name='featured', blank=True) + videos = models.ManyToManyField(Video, verbose_name=_('videos'), related_name='featured', blank=True) + playlists = models.ManyToManyField(Playlist, verbose_name=_('playlists'), related_name='featured', blank=True) + + def __unicode__(self): + return self.name diff --git a/app/organization/featured/templatetags/featured_tags.py b/app/organization/featured/templatetags/featured_tags.py new file mode 100644 index 00000000..79e32677 --- /dev/null +++ b/app/organization/featured/templatetags/featured_tags.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from mezzanine.pages.models import Page +from mezzanine.blog.models import BlogPost +from mezzanine.template import Library +from mezzanine_agenda.models import Event +from mezzanine.conf import settings +from random import shuffle + +from organization.festival.models import * +from organization.featured.models import * + +register = Library() + + +@register.filter +def subtract(value, arg): + return value - arg + +@register.as_tag +def featured_edito(*args): + qs = Page.objects.filter(slug="edito") + if qs: + return qs[0].get_content_model() + else: + return None + +@register.as_tag +def featured_events(*args): + featured = Featured.objects.all() + if featured: + return featured[0].events.order_by('start') + return None + +@register.as_tag +def featured(*args): + featured_list = [] + featured = Featured.objects.filter(id=settings.HOME_FEATURED_ID) + if featured: + featured = featured[0] + for post in featured.blogposts.all(): + featured_list.append(post) + for video in featured.videos.all(): + featured_list.append(video) + for artist in featured.artists.all(): + featured_list.append(artist) + for playlist in featured.playlists.all(): + featured_list.append(playlist) + shuffle(featured_list) + return featured_list + +@register.as_tag +def featured_breaking_news_content(*args): + featured = Featured.objects.filter(id=settings.BREAKING_NEWS_FEATURED_ID) + if featured: + featured = featured[0] + news = featured.pages.all() + if news: + return news[0].richtextpage.content + else: + return '' + return '' + +@register.filter +def get_class(obj): + return obj.__class__.__name__ + +@register.filter +def unique_posts(events): + post_list = [] + for event in events: + for post in event.blog_posts.all(): + print(post) + if not post in post_list: + post_list.append(post) + return post_list + +@register.filter +def no_parents(events): + return events.filter(parent=None) diff --git a/app/organization/featured/tests.py b/app/organization/featured/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/featured/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/featured/urls.py b/app/organization/featured/urls.py new file mode 100644 index 00000000..cea975ae --- /dev/null +++ b/app/organization/featured/urls.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + + +urlpatterns = [ + +] diff --git a/app/organization/featured/views.py b/app/organization/featured/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/app/organization/featured/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/organization/festival/__init__.py b/app/organization/festival/__init__.py new file mode 100644 index 00000000..e5988d7f --- /dev/null +++ b/app/organization/festival/__init__.py @@ -0,0 +1,7 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +default_app_config = 'organization.festival.apps.FestivalConfig' diff --git a/app/organization/festival/admin.py b/app/organization/festival/admin.py new file mode 100644 index 00000000..eac37c67 --- /dev/null +++ b/app/organization/festival/admin.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals + +from copy import deepcopy + +from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ + +from mezzanine_agenda.models import Event, EventLocation +from mezzanine_agenda.admin import * + +from mezzanine.conf import settings +from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin + +from organization.festival.models import * + + +class ArtistAdmin(admin.ModelAdmin): + + model = Artist + + +class ArtistAdminDisplayable(DisplayableAdmin): + + fieldsets = deepcopy(ArtistAdmin.fieldsets) + + +admin.site.register(Artist, ArtistAdminDisplayable) diff --git a/app/organization/festival/apps.py b/app/organization/festival/apps.py new file mode 100644 index 00000000..0ab1f17a --- /dev/null +++ b/app/organization/festival/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class FestivalConfig(AppConfig): + + name = 'organization.festival' + label = 'organization festival app' diff --git a/app/organization/festival/migrations/0001_initial.py b/app/organization/festival/migrations/0001_initial.py new file mode 100644 index 00000000..f74881be --- /dev/null +++ b/app/organization/festival/migrations/0001_initial.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import mezzanine.core.fields +import mezzanine.utils.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ] + + operations = [ + migrations.CreateModel( + name='Artist', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('title_fr', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('title_en', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('description_fr', models.TextField(blank=True, null=True, verbose_name='Description')), + ('description_en', models.TextField(blank=True, null=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('content_fr', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('content_en', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('photo', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='photo')), + ('photo_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo credits')), + ('photo_alignment', models.CharField(blank=True, choices=[('left', 'left'), ('center', 'center'), ('right', 'right')], default='left', max_length=32, verbose_name='photo alignment')), + ('photo_description', models.TextField(blank=True, verbose_name='photo description')), + ('photo_card', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='card photo')), + ('photo_card_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo card credits')), + ('photo_slider', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='slider photo')), + ('photo_slider_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo slider credits')), + ('first_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='last name')), + ('bio', mezzanine.core.fields.RichTextField(blank=True, verbose_name='biography')), + ('bio_fr', mezzanine.core.fields.RichTextField(blank=True, null=True, verbose_name='biography')), + ('bio_en', mezzanine.core.fields.RichTextField(blank=True, null=True, verbose_name='biography')), + ('site', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'ordering': ['last_name'], + 'verbose_name': 'artist', + }, + bases=(mezzanine.utils.models.AdminThumbMixin, models.Model), + ), + ] diff --git a/app/organization/festival/migrations/__init__.py b/app/organization/festival/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/festival/models.py b/app/organization/festival/models.py new file mode 100644 index 00000000..430ac4bf --- /dev/null +++ b/app/organization/festival/models.py @@ -0,0 +1,36 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, reverse_lazy +from django.conf import settings + +from mezzanine.core.models import RichText, Displayable, Slugged +from mezzanine.core.fields import RichTextField, OrderField, FileField +from mezzanine.utils.models import AdminThumbMixin, upload_to +from mezzanine.blog.models import BlogPost +from mezzanine.pages.models import Page + +from organization.core.models import * +from organization.media.models import * + +import requests +from pyquery import PyQuery as pq + + +class Artist(Displayable, RichText, AdminThumbMixin, Photo): + """Artist""" + + first_name = models.CharField(_('first name'), max_length=255, blank=True, null=True) + last_name = models.CharField(_('last name'), max_length=255, blank=True, null=True) + bio = RichTextField(_('biography'), blank=True) + search_fields = ("title", "bio") + + class Meta: + verbose_name = _('artist') + ordering = ['last_name',] + + def __unicode__(self): + return self.title + + @property + def name(self): + return self.title diff --git a/app/organization/festival/routers.py b/app/organization/festival/routers.py new file mode 100644 index 00000000..6bde6a32 --- /dev/null +++ b/app/organization/festival/routers.py @@ -0,0 +1,31 @@ + +class FestivalRouter(object): + """ + A router to control all database operations on models in festival + """ + + def db_for_read(self, model, **hints): + if model._meta.app_label == 'festival': + return 'default' + return None + + def db_for_write(self, model, **hints): + if model._meta.app_label == 'festival': + return 'default' + return None + + def allow_relation(self, obj1, obj2, **hints): + if obj1._meta.app_label == 'festival' or \ + obj2._meta.app_label == 'festival': + return True + return None + + # def allow_migrate(self, db, app_label, model=None, **hints): + # if app_label == 'festival': + # return db == 'default' + # return None + + def allow_migrate(self, db, app_label, model_name=None, **hints): + if 'target_db' in hints: + return db == hints['target_db'] + return True diff --git a/app/organization/festival/templatetags/festival_tags.py b/app/organization/festival/templatetags/festival_tags.py new file mode 100644 index 00000000..5f3c9583 --- /dev/null +++ b/app/organization/festival/templatetags/festival_tags.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from mezzanine.pages.models import Page +from mezzanine.blog.models import BlogPost +from mezzanine.template import Library +from mezzanine_agenda.models import Event +from mezzanine.conf import settings +from random import shuffle + +from organization.festival.models import * +from organization.magazine.models import * + +register = Library() + + +@register.filter +def subtract(value, arg): + return value - arg + +@register.as_tag +def featured_edito(*args): + qs = Page.objects.filter(slug="edito") + if qs: + return qs[0].get_content_model() + else: + return None + +@register.as_tag +def featured_events(*args): + featured = Featured.objects.all() + if featured: + return featured[0].events.order_by('start') + return None + +@register.as_tag +def featured(*args): + featured_list = [] + featured = Featured.objects.filter(id=settings.HOME_FEATURED_ID) + if featured: + featured = featured[0] + for post in featured.blogposts.all(): + featured_list.append(post) + for video in featured.videos.all(): + featured_list.append(video) + for artist in featured.artists.all(): + featured_list.append(artist) + for playlist in featured.playlists.all(): + featured_list.append(playlist) + shuffle(featured_list) + return featured_list + +@register.as_tag +def featured_breaking_news_content(*args): + featured = Featured.objects.filter(id=settings.BREAKING_NEWS_FEATURED_ID) + if featured: + featured = featured[0] + news = featured.pages.all() + if news: + return news[0].richtextpage.content + else: + return '' + return '' + +@register.filter +def get_class(obj): + return obj.__class__.__name__ + +@register.filter +def unique_posts(events): + post_list = [] + for event in events: + for post in event.blog_posts.all(): + print(post) + if not post in post_list: + post_list.append(post) + return post_list + +@register.filter +def no_parents(events): + return events.filter(parent=None) diff --git a/app/organization/festival/tests.py b/app/organization/festival/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/festival/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/festival/translation.py b/app/organization/festival/translation.py new file mode 100644 index 00000000..b9e4936b --- /dev/null +++ b/app/organization/festival/translation.py @@ -0,0 +1,8 @@ +from modeltranslation.translator import register, TranslationOptions + +from organization.festival.models import * + +@register(Artist) +class ArtistTranslationOptions(TranslationOptions): + + fields = ('title', 'description', 'bio', 'content') diff --git a/app/organization/festival/urls.py b/app/organization/festival/urls.py new file mode 100644 index 00000000..c13eedb4 --- /dev/null +++ b/app/organization/festival/urls.py @@ -0,0 +1,16 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + +from organization.festival.views import * + + +urlpatterns = [ + url(r'^artists/$', ArtistListView.as_view(), name="festival-artist-list"), + url(r'^artists/detail/(?P.*)/$', ArtistDetailView.as_view(), name="festival-artist-detail"), +] diff --git a/app/organization/festival/views.py b/app/organization/festival/views.py new file mode 100644 index 00000000..beba1813 --- /dev/null +++ b/app/organization/festival/views.py @@ -0,0 +1,33 @@ +from django.shortcuts import render +from django.views.generic import * +from django.views.generic.base import * +from django.shortcuts import get_object_or_404 + +from organization.festival.models import * +from mezzanine_agenda.models import EventLocation + +from organization.core.views import * + + +class ArtistListView(ListView): + + model = Artist + template_name='festival/artist_list.html' + + def get_queryset(self, **kwargs): + return self.model.objects.published() + + def get_context_data(self, **kwargs): + context = super(ArtistListView, self).get_context_data(**kwargs) + return context + + +class ArtistDetailView(SlugMixin, DetailView): + + model = Artist + template_name='festival/artist_detail.html' + context_object_name = 'artist' + + def get_context_data(self, **kwargs): + context = super(ArtistDetailView, self).get_context_data(**kwargs) + return context diff --git a/app/organization/magazine/__init__.py b/app/organization/magazine/__init__.py new file mode 100644 index 00000000..043bee89 --- /dev/null +++ b/app/organization/magazine/__init__.py @@ -0,0 +1,10 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +from mezzanine import __version__ # noqa + + +default_app_config = 'organization.magazine.apps.MagazineConfig' diff --git a/app/organization/magazine/admin.py b/app/organization/magazine/admin.py new file mode 100644 index 00000000..aa592f08 --- /dev/null +++ b/app/organization/magazine/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +from organization.magazine.models import * diff --git a/app/organization/magazine/apps.py b/app/organization/magazine/apps.py new file mode 100644 index 00000000..78483d70 --- /dev/null +++ b/app/organization/magazine/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class MagazineConfig(AppConfig): + + name = 'organization.magazine' + label = 'organization magazine' diff --git a/app/organization/magazine/migrations/0001_initial.py b/app/organization/magazine/migrations/0001_initial.py new file mode 100644 index 00000000..683c85db --- /dev/null +++ b/app/organization/magazine/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-06 16:48 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ], + options={ + 'verbose_name': 'category', + }, + ), + migrations.CreateModel( + name='Topic', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ], + options={ + 'verbose_name': 'topic', + }, + ), + ] diff --git a/app/organization/magazine/migrations/0002_article.py b/app/organization/magazine/migrations/0002_article.py new file mode 100644 index 00000000..63704801 --- /dev/null +++ b/app/organization/magazine/migrations/0002_article.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 10:01 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0003_auto_20151223_1313'), + ('organization magazine', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Article', + fields=[ + ('blogpost_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='blog.BlogPost')), + ('sub_title', models.CharField(blank=True, max_length=1000, verbose_name='sub title')), + ('sub_title_fr', models.CharField(blank=True, max_length=1000, null=True, verbose_name='sub title')), + ('sub_title_en', models.CharField(blank=True, max_length=1000, null=True, verbose_name='sub title')), + ], + options={ + 'verbose_name': 'article', + }, + bases=('blog.blogpost',), + ), + ] diff --git a/app/organization/magazine/migrations/__init__.py b/app/organization/magazine/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/magazine/models.py b/app/organization/magazine/models.py new file mode 100644 index 00000000..412eaf01 --- /dev/null +++ b/app/organization/magazine/models.py @@ -0,0 +1,40 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, reverse_lazy + +from mezzanine.blog.models import * + +from mezzanine_agenda.models import Event + +from organization.magazine.models import * +from organization.core.models import * + + +class Article(BlogPost): + + sub_title = models.CharField(_('sub title'), blank=True, max_length=1000) + + class Meta: + verbose_name = _('article') + + +class Category(Named): + """(Category description)""" + + class Meta: + verbose_name = _('category') + + def __unicode__(self): + return self.name + + +class Topic(Named): + """(Topic description)""" + + class Meta: + verbose_name = _('topic') + + def __unicode__(self): + return self.name diff --git a/app/organization/magazine/tests.py b/app/organization/magazine/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/magazine/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/magazine/translation.py b/app/organization/magazine/translation.py new file mode 100644 index 00000000..db66e9b1 --- /dev/null +++ b/app/organization/magazine/translation.py @@ -0,0 +1,13 @@ +from modeltranslation.translator import translator, register, TranslationOptions +from mezzanine.pages.models import Page, RichText +from modeltranslation.translator import TranslationOptions +from mezzanine.core.translation import (TranslatedSlugged, + TranslatedDisplayable, + TranslatedRichText) +from organization.magazine.models import Article + +@register(Article) +#class ArticleTranslationOptions(TranslatedDisplayable, TranslatedRichText): +class ArticleTranslationOptions(TranslationOptions): + + fields = ('sub_title',) diff --git a/app/organization/magazine/urls.py b/app/organization/magazine/urls.py new file mode 100644 index 00000000..cea975ae --- /dev/null +++ b/app/organization/magazine/urls.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + + +urlpatterns = [ + +] diff --git a/app/organization/magazine/views.py b/app/organization/magazine/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/app/organization/magazine/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/organization/media/__init__.py b/app/organization/media/__init__.py new file mode 100644 index 00000000..a4e2211d --- /dev/null +++ b/app/organization/media/__init__.py @@ -0,0 +1,10 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +from mezzanine import __version__ # noqa + + +default_app_config = 'organization.media.apps.MediaConfig' diff --git a/app/organization/media/admin.py b/app/organization/media/admin.py new file mode 100644 index 00000000..9ea7f15c --- /dev/null +++ b/app/organization/media/admin.py @@ -0,0 +1,39 @@ +from copy import deepcopy +from django.contrib import admin +from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin +from organization.media.models import * + + +class VideoAdmin(admin.ModelAdmin): + + model = Video + + +class VideoAdminDisplayable(DisplayableAdmin): + + fieldsets = deepcopy(VideoAdmin.fieldsets) + #filter_horizontal = ['artists'] + + +class AudioAdmin(admin.ModelAdmin): + + model = Audio + + +class AudioAdminDisplayable(DisplayableAdmin): + + fieldsets = deepcopy(AudioAdmin.fieldsets) + # filter_horizontal = ['artists'] + + +class PlaylistAdmin(admin.ModelAdmin): + + model = Playlist + list_display = ('__unicode__',) + filter_horizontal = ['audios'] + + +admin.site.register(Video, VideoAdminDisplayable) +admin.site.register(Audio, AudioAdminDisplayable) +admin.site.register(Playlist, PlaylistAdmin) +admin.site.register(VideoCategory) diff --git a/app/organization/media/apps.py b/app/organization/media/apps.py new file mode 100644 index 00000000..d5dd59ac --- /dev/null +++ b/app/organization/media/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class MediaConfig(AppConfig): + + name = 'organization.media' + label = 'organization media' diff --git a/app/organization/media/migrations/0001_initial.py b/app/organization/media/migrations/0001_initial.py new file mode 100644 index 00000000..32ee5682 --- /dev/null +++ b/app/organization/media/migrations/0001_initial.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ] + + operations = [ + migrations.CreateModel( + name='Audio', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('title_fr', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('title_en', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('description_fr', models.TextField(blank=True, null=True, verbose_name='Description')), + ('description_en', models.TextField(blank=True, null=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('content_fr', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('content_en', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('media_id', models.CharField(max_length=128, verbose_name='media id')), + ('open_source_url', models.URLField(blank=True, max_length=1024, verbose_name='open source URL')), + ('closed_source_url', models.URLField(blank=True, max_length=1024, verbose_name='closed source URL')), + ('poster_url', models.URLField(blank=True, max_length=1024, verbose_name='poster')), + ('site', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'verbose_name': 'audio', + }, + ), + migrations.CreateModel( + name='Playlist', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=512, verbose_name='title')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('audios', models.ManyToManyField(blank=True, related_name='playlists', to='organization media.Audio', verbose_name='audios')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Video', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('title_fr', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('title_en', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('description_fr', models.TextField(blank=True, null=True, verbose_name='Description')), + ('description_en', models.TextField(blank=True, null=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('content_fr', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('content_en', mezzanine.core.fields.RichTextField(null=True, verbose_name='Content')), + ('media_id', models.CharField(max_length=128, verbose_name='media id')), + ('open_source_url', models.URLField(blank=True, max_length=1024, verbose_name='open source URL')), + ('closed_source_url', models.URLField(blank=True, max_length=1024, verbose_name='closed source URL')), + ('poster_url', models.URLField(blank=True, max_length=1024, verbose_name='poster')), + ], + options={ + 'verbose_name': 'video', + }, + ), + migrations.CreateModel( + name='VideoCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('site', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'verbose_name': 'video category', + }, + ), + migrations.AddField( + model_name='video', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='videos', to='organization media.VideoCategory', verbose_name='category'), + ), + migrations.AddField( + model_name='video', + name='site', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site'), + ), + ] diff --git a/app/organization/media/migrations/__init__.py b/app/organization/media/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/media/models.py b/app/organization/media/models.py new file mode 100644 index 00000000..c4dfffa0 --- /dev/null +++ b/app/organization/media/models.py @@ -0,0 +1,128 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from mezzanine.core.models import RichText, Displayable, Slugged +from mezzanine.core.fields import RichTextField, OrderField, FileField +from mezzanine.utils.models import AdminThumbMixin, upload_to + +from mezzanine_agenda.models import Event +from organization.core.models import * + + +ALIGNMENT_CHOICES = (('left', _('left')), ('center', _('center')), ('right', _('right'))) +MEDIA_BASE_URL = getattr(settings, 'MEDIA_BASE_URL', 'http://medias.ircam.fr/embed/media/') + + +class Photo(models.Model): + """Photo bundle with credits""" + + photo = FileField(_('photo'), upload_to='images/photos', max_length=1024, blank=True, format="Image") + photo_credits = models.CharField(_('photo credits'), max_length=255, blank=True, null=True) + photo_alignment = models.CharField(_('photo alignment'), choices=ALIGNMENT_CHOICES, max_length=32, default="left", blank=True) + photo_description = models.TextField(_('photo description'), blank=True) + + photo_card = FileField(_('card photo'), upload_to='images/photos/card', max_length=1024, blank=True, format="Image") + photo_card_credits = models.CharField(_('photo card credits'), max_length=255, blank=True, null=True) + + photo_slider = FileField(_('slider photo'), upload_to='images/photos/slider', max_length=1024, blank=True, format="Image") + photo_slider_credits = models.CharField(_('photo slider credits'), max_length=255, blank=True, null=True) + + class Meta: + abstract = True + + @property + def card(self): + if self.photo_card: + return self.photo_card + else: + return self.photo + + +class Media(Displayable, RichText): + """Media""" + + media_id = models.CharField(_('media id'), max_length=128) + open_source_url = models.URLField(_('open source URL'), max_length=1024, blank=True) + closed_source_url = models.URLField(_('closed source URL'), max_length=1024, blank=True) + poster_url = models.URLField(_('poster'), max_length=1024, blank=True) + + class Meta: + abstract = True + + def __unicode__(self): + return self.title + + @property + def uri(self): + return MEDIA_BASE_URL + self.media_id + + def get_html(self): + r = requests.get(self.uri) + return r.content + + def clean(self): + super(Media, self).clean() + self.q = pq(self.get_html()) + sources = self.q('source') + for source in sources: + if self.open_source_mime_type in source.attrib['type']: + self.open_source_url = source.attrib['src'] + elif self.closed_source_mime_type in source.attrib['type']: + self.closed_source_url = source.attrib['src'] + video = self.q('video') + if len(video): + if 'poster' in video[0].attrib.keys(): + self.poster_url = video[0].attrib['poster'] + + +class Audio(Media): + """Audio""" + + open_source_mime_type = 'audio/ogg' + closed_source_mime_type = 'audio/mp4' + + class Meta: + verbose_name = _('audio') + + def get_absolute_url(self): + return reverse("festival-video-detail", kwargs={"slug": self.slug}) + + +class Video(Media): + """Video""" + + open_source_mime_type = 'video/webm' + closed_source_mime_type = 'video/mp4' + category = models.ForeignKey('VideoCategory', related_name='videos', verbose_name=_('category'), blank=True, null=True, on_delete=models.SET_NULL) + + class Meta: + verbose_name = _('video') + + @property + def html(self): + #TODO: get html content from medias.ircam.fr with request module + pass + + def get_absolute_url(self): + return reverse("festival-video-detail", kwargs={"slug": self.slug}) + + +class VideoCategory(Slugged): + """Video Category""" + + class Meta: + verbose_name = _('video category') + + def count(self): + return self.videos.published().count()+1 + + +class Playlist(Titled): + """(Playlist description)""" + + audios = models.ManyToManyField('Audio', verbose_name=_('audios'), related_name='playlists', blank=True) + + def __str__(self): + return self.title diff --git a/app/organization/media/tests.py b/app/organization/media/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/media/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/media/translation.py b/app/organization/media/translation.py new file mode 100644 index 00000000..bd097142 --- /dev/null +++ b/app/organization/media/translation.py @@ -0,0 +1,15 @@ +from modeltranslation.translator import translator, register, TranslationOptions + +from organization.media.models import * + + +@register(Video) +class VideoTranslationOptions(TranslationOptions): + + fields = ('title', 'description', 'content') + + +@register(Audio) +class AudioTranslationOptions(TranslationOptions): + + fields = ('title', 'description', 'content') diff --git a/app/organization/media/urls.py b/app/organization/media/urls.py new file mode 100644 index 00000000..4b0faa6a --- /dev/null +++ b/app/organization/media/urls.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + +from organization.media.views import * + + +urlpatterns = [ + url(r'^videos/$', VideoListView.as_view(), name="festival-video-list"), + url(r'^videos/detail/(?P.*)/$', VideoDetailView.as_view(), name="festival-video-detail"), + url(r'^videos/category/(?P.*)/$', VideoListCategoryView.as_view(), name="festival-video-list-category"), +] diff --git a/app/organization/media/views.py b/app/organization/media/views.py new file mode 100644 index 00000000..933be23d --- /dev/null +++ b/app/organization/media/views.py @@ -0,0 +1,41 @@ +from django.shortcuts import render + +from organization.media.models import * +from organization.core.views import * + + +class VideoListView(ListView): + + model = Video + template_name='festival/video_list.html' + + def get_queryset(self, **kwargs): + return self.model.objects.published() + + def get_context_data(self, **kwargs): + context = super(VideoListView, self).get_context_data(**kwargs) + context['categories'] = VideoCategory.objects.all() + return context + + +class VideoDetailView(SlugMixin, DetailView): + + model = Video + template_name='festival/video_detail.html' + context_object_name = 'video' + + def get_context_data(self, **kwargs): + context = super(VideoDetailView, self).get_context_data(**kwargs) + return context + + +class VideoListCategoryView(VideoListView): + + def get_queryset(self): + self.category = VideoCategory.objects.get(slug=self.kwargs['slug']) + return self.model.objects.filter(category=self.category) + + def get_context_data(self, **kwargs): + context = super(VideoListCategoryView, self).get_context_data(**kwargs) + context['category'] = self.category + return context diff --git a/app/organization/project/__init__.py b/app/organization/project/__init__.py new file mode 100644 index 00000000..03586203 --- /dev/null +++ b/app/organization/project/__init__.py @@ -0,0 +1,10 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +from mezzanine import __version__ # noqa + + +default_app_config = 'organization.project.apps.ProjectConfig' diff --git a/app/organization/project/admin.py b/app/organization/project/admin.py new file mode 100644 index 00000000..988416d5 --- /dev/null +++ b/app/organization/project/admin.py @@ -0,0 +1,25 @@ +from django.contrib import admin +from mezzanine.blog.admin import BlogPostAdmin +from organization.magazine.models import Article +#from custom.admin import SubTitleAdmin +from copy import deepcopy + +from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ +from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin + +# class ArticleAdmin(BlogPostAdmin, SubTitleAdmin): +# +# model = Article + +#admin.site.register(Article, BlogPostAdmin) +class ArticleAdmin(admin.ModelAdmin): + + model = Article + + +class ArticleAdminDisplayable(DisplayableAdmin): + + fieldsets = deepcopy(ArticleAdmin.fieldsets) + +admin.site.register(Article, ArticleAdminDisplayable) diff --git a/app/organization/project/apps.py b/app/organization/project/apps.py new file mode 100644 index 00000000..34d023b2 --- /dev/null +++ b/app/organization/project/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class ProjectConfig(AppConfig): + + name = 'organization.project' + label = 'organization project' diff --git a/app/organization/project/migrations/0001_initial.py b/app/organization/project/migrations/0001_initial.py new file mode 100644 index 00000000..c96edf5f --- /dev/null +++ b/app/organization/project/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 08:53 +from __future__ import unicode_literals + +from django.db import migrations, models +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Project', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/app/organization/project/migrations/0002_auto_20160707_1053.py b/app/organization/project/migrations/0002_auto_20160707_1053.py new file mode 100644 index 00000000..5d78ef15 --- /dev/null +++ b/app/organization/project/migrations/0002_auto_20160707_1053.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 08:53 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('organization project', '0001_initial'), + ('organization team', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='partners', + field=models.ManyToManyField(to='organization team.Organization', verbose_name='organizations'), + ), + migrations.AddField( + model_name='project', + name='persons', + field=models.ManyToManyField(to='organization team.Person', verbose_name='persons'), + ), + migrations.AddField( + model_name='project', + name='site', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site'), + ), + ] diff --git a/app/organization/project/migrations/__init__.py b/app/organization/project/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/project/models.py b/app/organization/project/models.py new file mode 100644 index 00000000..31a7792d --- /dev/null +++ b/app/organization/project/models.py @@ -0,0 +1,18 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from mezzanine.core.models import RichText, Displayable, Slugged + +from organization.team.models import Person, Organization + + +class Project(Displayable, RichText): + """(Project description)""" + + persons = models.ManyToManyField(Person, verbose_name=_('persons')) + partners = models.ManyToManyField(Organization, verbose_name=_('organizations')) + + def __unicode__(self): + return self.title diff --git a/app/organization/project/tests.py b/app/organization/project/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/project/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/project/urls.py b/app/organization/project/urls.py new file mode 100644 index 00000000..cea975ae --- /dev/null +++ b/app/organization/project/urls.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + + +urlpatterns = [ + +] diff --git a/app/organization/project/views.py b/app/organization/project/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/app/organization/project/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/organization/team/__init__.py b/app/organization/team/__init__.py new file mode 100644 index 00000000..2de9352f --- /dev/null +++ b/app/organization/team/__init__.py @@ -0,0 +1,10 @@ +""" +Provides abstract models and admin features used throughout the various +Mezzanine apps. +""" +from __future__ import unicode_literals + +from mezzanine import __version__ # noqa + + +default_app_config = 'organization.team.apps.teamConfig' diff --git a/app/organization/team/admin.py b/app/organization/team/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/app/organization/team/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/app/organization/team/apps.py b/app/organization/team/apps.py new file mode 100644 index 00000000..81f7caf5 --- /dev/null +++ b/app/organization/team/apps.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class teamConfig(AppConfig): + + name = 'organization.team' + label = 'organization team' diff --git a/app/organization/team/migrations/0001_initial.py b/app/organization/team/migrations/0001_initial.py new file mode 100644 index 00000000..4b4382b0 --- /dev/null +++ b/app/organization/team/migrations/0001_initial.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 08:53 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django_countries.fields +import mezzanine.core.fields +import mezzanine.utils.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Activity', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date_begin', models.DateField(blank=True, null=True, verbose_name='begin date')), + ('date_end', models.DateField(blank=True, null=True, verbose_name='end date')), + ('role', models.CharField(blank=True, max_length=512, verbose_name='role')), + ('work', models.TextField(blank=True, verbose_name='work')), + ], + ), + migrations.CreateModel( + name='Address', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('address', models.TextField(blank=True, verbose_name='description')), + ('postal_code', models.CharField(blank=True, max_length=16, verbose_name='postal code')), + ('country', django_countries.fields.CountryField(max_length=2, verbose_name='country')), + ], + ), + migrations.CreateModel( + name='Department', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('url', models.URLField(blank=True, max_length=512, verbose_name='URL')), + ('weaving_class', models.CharField(blank=True, max_length=64, verbose_name='weaving class')), + ], + options={ + 'verbose_name': 'department', + }, + ), + migrations.CreateModel( + name='Link', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField(verbose_name='URL')), + ], + ), + migrations.CreateModel( + name='LinkType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256, verbose_name='Name')), + ('slug', models.SlugField(blank=True, help_text='Use this field to define a simple identifier that can be used to style the different link types (i.e. assign social media icons to them)', max_length=256, verbose_name='Slug')), + ('ordering', models.PositiveIntegerField(blank=True, null=True, verbose_name='Ordering')), + ], + options={ + 'ordering': ['ordering'], + }, + ), + migrations.CreateModel( + name='Modelname', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='Nationality', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='name')), + ], + ), + migrations.CreateModel( + name='OrganizationType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ], + options={ + 'verbose_name': 'organization type', + }, + ), + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('content', mezzanine.core.fields.RichTextField(verbose_name='Content')), + ('photo', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='photo')), + ('photo_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo credits')), + ('photo_alignment', models.CharField(blank=True, choices=[('left', 'left'), ('center', 'center'), ('right', 'right')], default='left', max_length=32, verbose_name='photo alignment')), + ('photo_description', models.TextField(blank=True, verbose_name='photo description')), + ('photo_card', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='card photo')), + ('photo_card_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo card credits')), + ('photo_slider', mezzanine.core.fields.FileField(blank=True, max_length=1024, verbose_name='slider photo')), + ('photo_slider_credits', models.CharField(blank=True, max_length=255, null=True, verbose_name='photo slider credits')), + ('person_title', models.CharField(blank=True, choices=[('Dr', 'Dr'), ('Prof', 'Prof'), ('Prof Dr', 'Prof Dr')], max_length=16, verbose_name='title')), + ('gender', models.CharField(blank=True, choices=[('male', 'male'), ('female', 'female')], max_length=16, verbose_name='gender')), + ('first_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='last name')), + ('birthday', models.DateField(blank=True, verbose_name='birthday')), + ('site', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + 'abstract': False, + }, + bases=(mezzanine.utils.models.AdminThumbMixin, models.Model), + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='organization team.Department', verbose_name='department')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Organization', + fields=[ + ('address_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization team.Address')), + ('name', models.CharField(max_length=512, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('url', models.URLField(blank=True, max_length=512, verbose_name='URL')), + ('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='organization team.OrganizationType', verbose_name='organization type')), + ], + options={ + 'verbose_name': 'organization', + }, + bases=('organization team.address', models.Model), + ), + migrations.AddField( + model_name='link', + name='link_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organization team.LinkType', verbose_name='Link type'), + ), + migrations.AddField( + model_name='link', + name='person', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organization team.Person', verbose_name='Person'), + ), + migrations.AddField( + model_name='activity', + name='person', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organization team.Person', verbose_name='person'), + ), + migrations.AddField( + model_name='activity', + name='teams', + field=models.ManyToManyField(to='organization team.Team', verbose_name='teams'), + ), + migrations.AddField( + model_name='person', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='organization team.Organization', verbose_name='organization'), + ), + migrations.AddField( + model_name='department', + name='organization', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organization team.Organization', verbose_name='organization'), + ), + ] diff --git a/app/organization/team/migrations/0002_auto_20160707_1614.py b/app/organization/team/migrations/0002_auto_20160707_1614.py new file mode 100644 index 00000000..28aa1de8 --- /dev/null +++ b/app/organization/team/migrations/0002_auto_20160707_1614.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-07 14:14 +from __future__ import unicode_literals + +from django.db import migrations +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization team', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='Modelname', + ), + migrations.AlterModelOptions( + name='person', + options={'ordering': ['last_name'], 'verbose_name': 'person'}, + ), + migrations.AddField( + model_name='person', + name='bio', + field=mezzanine.core.fields.RichTextField(blank=True, verbose_name='biography'), + ), + ] diff --git a/app/organization/team/migrations/__init__.py b/app/organization/team/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/organization/team/models.py b/app/organization/team/models.py new file mode 100644 index 00000000..139ccfe1 --- /dev/null +++ b/app/organization/team/models.py @@ -0,0 +1,202 @@ +from __future__ import unicode_literals + +import os +import re +import pwd +import time +import urllib +import string +import datetime +import mimetypes + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse, reverse_lazy +from django.conf import settings +from django.contrib.auth.models import User + +from mezzanine.core.models import RichText, Displayable, Slugged +from mezzanine.core.fields import RichTextField, OrderField, FileField +from mezzanine.utils.models import AdminThumbMixin, upload_to + +from django_countries.fields import CountryField + +from organization.media.models import Photo +from organization.core.models import Named + +# Hack to have these strings translated +mr = _('Mr') +mrs = _('Ms') + +GENDER_CHOICES = [ + ('male', _('male')), + ('female', _('female')), +] + +TITLE_CHOICES = [ + ('Dr', _('Dr')), + ('Prof', _('Prof')), + ('Prof Dr', _('Prof Dr')), +] + +ALIGNMENT_CHOICES = (('left', _('left')), ('right', _('right'))) + + + +class Address(models.Model): + """(Address description)""" + + address = models.TextField(_('description'), blank=True) + postal_code = models.CharField(_('postal code'), max_length=16, blank=True) + country = CountryField(_('country')) + + def __unicode__(self): + return u"Address" + + class Meta: + abstract = True + + +class Organization(Named, Address): + """(Organization description)""" + + type = models.ForeignKey('OrganizationType', verbose_name=_('organization type'), blank=True, null=True, on_delete=models.SET_NULL) + url = models.URLField(_('URL'), max_length=512, blank=True) + + def __unicode__(self): + return self.name + + class Meta: + verbose_name = _('organization') + + +class OrganizationType(Named): + """(OrganizationType description)""" + + class Meta: + verbose_name = _('organization type') + + +class Department(Named): + """(Department description)""" + + organization = models.ForeignKey('Organization', verbose_name=_('organization')) + url = models.URLField(_('URL'), max_length=512, blank=True) + weaving_class = models.CharField(_('weaving class'), max_length=64, blank=True) + + def __unicode__(self): + return self.name + + class Meta: + verbose_name = _('department') + + +class Team(Named): + """(Team description)""" + + department = models.ForeignKey('Department', verbose_name=_('department'), blank=True, null=True, on_delete=models.SET_NULL) + + def __unicode__(self): + return u"Team" + + +class Person(Displayable, RichText, AdminThumbMixin, Photo): + """(Person description)""" + + user = models.ForeignKey(User, verbose_name=_('user'), blank=True, null=True, on_delete=models.SET_NULL) + person_title = models.CharField(_('title'), max_length=16, choices=TITLE_CHOICES, blank=True) + gender = models.CharField(_('gender'), max_length=16, choices=GENDER_CHOICES, blank=True) + first_name = models.CharField(_('first name'), max_length=255, blank=True, null=True) + last_name = models.CharField(_('last name'), max_length=255, blank=True, null=True) + birthday = models.DateField(_('birthday'), blank=True) + organization = models.ForeignKey('Organization', verbose_name=_('organization'), blank=True, null=True, on_delete=models.SET_NULL) + bio = RichTextField(_('biography'), blank=True) + + class Meta: + verbose_name = _('person') + ordering = ['last_name',] + + def __unicode__(self): + return ' '.join((self.user.first_name, self.user.last_name)) + + # def get_absolute_url(self): + # return reverse("festival-artist-detail", kwargs={'slug': self.slug}) + + def set_names(self): + names = self.title.split(' ') + if len(names) == 1: + self.first_name = '' + self.last_name = names[0] + elif len(names) == 2: + self.first_name = names[0] + self.last_name = names[1] + else: + self.first_name = names[0] + self.last_name = ' '.join(names[1:]) + + def clean(self): + super(Person, self).clean() + self.set_names() + + def save(self, *args, **kwargs): + self.set_names() + super(Person, self).save(*args, **kwargs) + + +class Nationality(models.Model): + """(Nationality description)""" + + name = models.CharField(_('name'), max_length=128) + + def __unicode__(self): + return self.name + + +class Link(models.Model): + """A person can have many links.""" + + person = models.ForeignKey('Person', verbose_name=_('Person')) + link_type = models.ForeignKey('LinkType', verbose_name=_('Link type')) + url = models.URLField(verbose_name=_('URL')) + + def __str__(self): + return self.url + + +class LinkType(models.Model): + """ + A link type could be ``Facebook`` or ``Twitter`` or ``Website``. + This is masterdata that should be created by the admins when the site is + deployed for the first time. + :ordering: Enter numbers here if you want links to be displayed in a + special order. + """ + + name = models.CharField(max_length=256, verbose_name=_('Name')) + slug = models.SlugField(max_length=256, verbose_name=_('Slug'), help_text=_( + 'Use this field to define a simple identifier that can be used' + ' to style the different link types (i.e. assign social media' + ' icons to them)'), + blank=True, + ) + ordering = models.PositiveIntegerField(verbose_name=_('Ordering'), null=True, blank=True) + + class Meta: + ordering = ['ordering', ] + + def __str__(self): + return self.name + + +class Activity(models.Model): + """(Activity description)""" + + person = models.ForeignKey('Person', verbose_name=_('person')) + teams = models.ManyToManyField('Team', verbose_name=_('teams')) + date_begin = models.DateField(_('begin date'), null=True, blank=True) + date_end = models.DateField(_('end date'), null=True, blank=True) + role = models.CharField(_('role'), blank=True, max_length=512) + work = models.TextField(_('work'), blank=True) + + def __unicode__(self): + return ' - '.join((self.person, self.role, self.date_begin, self.date_end)) diff --git a/app/organization/team/tests.py b/app/organization/team/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app/organization/team/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/organization/team/urls.py b/app/organization/team/urls.py new file mode 100644 index 00000000..ace2c073 --- /dev/null +++ b/app/organization/team/urls.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + +from organization.team.views import * + + +urlpatterns = [ + +] diff --git a/app/organization/team/views.py b/app/organization/team/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/app/organization/team/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/organization/urls.py b/app/organization/urls.py new file mode 100644 index 00000000..adafa016 --- /dev/null +++ b/app/organization/urls.py @@ -0,0 +1,21 @@ +from __future__ import unicode_literals + +import django.views.i18n +from django.conf.urls import patterns, include, url +from django.conf.urls.i18n import i18n_patterns +from django.contrib import admin + +admin.autodiscover() + +from mezzanine.core.views import direct_to_template +from mezzanine.conf import settings + + +urlpatterns = [ + url("^", include('organization.core.urls')), + url("^festival/", include('organization.festival.urls')), + url("^magazine/", include('organization.magazine.urls')), + url("^media/", include('organization.media.urls')), + url("^project/", include('organization.project.urls')), + url("^team/", include('organization.team.urls')), +] diff --git a/app/settings.py b/app/settings.py index 18b8e05f..788d0eba 100644 --- a/app/settings.py +++ b/app/settings.py @@ -124,11 +124,6 @@ USE_L10N = True AUTHENTICATION_BACKENDS = ("mezzanine.core.auth_backends.MezzanineBackend",) -# The numeric mode to set newly-uploaded files to. The value should be -# a mode you'd pass directly to os.chmod. -FILE_UPLOAD_PERMISSIONS = 0o644 - -# MAX_UPLOAD_SIZE = 429916160 ############# # DATABASES # @@ -213,7 +208,6 @@ INSTALLED_APPS = [ "mezzanine.core", "mezzanine.generic", "mezzanine.pages", - "custom", "mezzanine.blog", "mezzanine.forms", "mezzanine.galleries", @@ -224,8 +218,13 @@ INSTALLED_APPS = [ 'djangobower', "meta", "mezzanine_agenda", - "festival", - "organization" + "organization.core", + "organization.team", + "organization.festival", + "organization.magazine", + "organization.media", + "organization.project", + "organization.featured", ] @@ -247,8 +246,9 @@ MIGRATION_MODULES = { } MODELTRANSLATION_TRANSLATION_FILES = ( - 'custom.translations', - 'translations', + 'organization.core.translation', + 'organization.festival.translation', + 'organization.magazine.translation' ) TEMPLATES = [{'APP_DIRS': True, @@ -291,7 +291,7 @@ MIDDLEWARE_CLASSES = ( # Uncomment the following if using any of the SSL settings: # "mezzanine.core.middleware.SSLRedirectMiddleware", "mezzanine.pages.middleware.PageMiddleware", - "mezzanine.core.middleware.FetchFromCacheMiddleware", + # "mezzanine.core.middleware.FetchFromCacheMiddleware", ) # Store these package names here as they may change in the future since diff --git a/app/templates/agenda/event_booking.html b/app/templates/agenda/event_booking.html index 05812b25..880ded65 100644 --- a/app/templates/agenda/event_booking.html +++ b/app/templates/agenda/event_booking.html @@ -1,5 +1,5 @@ {% extends "agenda/event_detail.html" %} -{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags festival_tags %} +{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags featured_tags %} {% block event_detail_postedby %} {% endblock %} diff --git a/app/templates/agenda/event_detail.html b/app/templates/agenda/event_detail.html index 4eb5e14a..e6df9efe 100644 --- a/app/templates/agenda/event_detail.html +++ b/app/templates/agenda/event_detail.html @@ -1,5 +1,5 @@ {% extends "agenda/event_list.html" %} -{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags festival_tags %} +{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags featured_tags %} {% block meta_title %}{{ event.meta_title }}{% endblock %} diff --git a/app/templates/agenda/event_iframe.html b/app/templates/agenda/event_iframe.html index 769f91d4..61f0befd 100644 --- a/app/templates/agenda/event_iframe.html +++ b/app/templates/agenda/event_iframe.html @@ -1,5 +1,5 @@ {% extends "agenda/event_detail.html" %} -{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags festival_tags %} +{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n future disqus_tags event_tags featured_tags %} {% block title %} {{ title }} diff --git a/app/templates/agenda/includes/event_metainfo.html b/app/templates/agenda/includes/event_metainfo.html index adfbb11c..c836e03d 100644 --- a/app/templates/agenda/includes/event_metainfo.html +++ b/app/templates/agenda/includes/event_metainfo.html @@ -1,4 +1,4 @@ -{% load i18n mezzanine_tags event_tags festival_tags %} +{% load i18n mezzanine_tags event_tags featured_tags %}
{{ event.start }} diff --git a/app/templates/agenda/includes/event_metainfo_slider.html b/app/templates/agenda/includes/event_metainfo_slider.html index adfda788..a748baff 100644 --- a/app/templates/agenda/includes/event_metainfo_slider.html +++ b/app/templates/agenda/includes/event_metainfo_slider.html @@ -1,4 +1,4 @@ -{% load i18n mezzanine_tags event_tags festival_tags %} +{% load i18n mezzanine_tags event_tags featured_tags %}
diff --git a/app/templates/base.html b/app/templates/base.html index 1cf860e8..7b35715f 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -1,6 +1,6 @@ -{% load i18n pages_tags mezzanine_tags staticfiles keyword_tags event_tags festival_tags %} +{% load i18n pages_tags mezzanine_tags staticfiles keyword_tags event_tags featured_tags %} {% get_language_info_list for LANGUAGES as languages %} diff --git a/app/templates/blog/blog_post_detail.html b/app/templates/blog/blog_post_detail.html index d052deee..9cb3f536 100644 --- a/app/templates/blog/blog_post_detail.html +++ b/app/templates/blog/blog_post_detail.html @@ -1,5 +1,5 @@ {% extends "blog/blog_post_list.html" %} -{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n disqus_tags festival_tags %} +{% load mezzanine_tags comment_tags keyword_tags rating_tags i18n disqus_tags featured_tags %} {% block meta_title %}{{ blog_post.meta_title }}{% endblock %} diff --git a/app/templates/includes/slider.html b/app/templates/includes/slider.html index 6c6a887a..fdfca906 100644 --- a/app/templates/includes/slider.html +++ b/app/templates/includes/slider.html @@ -1,4 +1,4 @@ -{% load i18n event_tags festival_tags mezzanine_tags %} +{% load i18n event_tags featured_tags mezzanine_tags %}
    {% featured_events as events %} diff --git a/app/templates/index.html b/app/templates/index.html index a315a7ce..b54288be 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n festival_tags %} +{% load i18n featured_tags %} {% block meta_title %}{% trans "Home" %}{% endblock %} diff --git a/app/templates/pages/basicpage.html b/app/templates/pages/basicpage.html new file mode 100644 index 00000000..f36fcf9e --- /dev/null +++ b/app/templates/pages/basicpage.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% load i18n mezzanine_tags keyword_tags featured_tags %} + +{% block meta_title %}{{ page.meta_title }}{% endblock %} + +{% block meta_keywords %}{% metablock %} +{% keywords_for page as keywords %} +{% for keyword in keywords %} + {% if not forloop.first %}, {% endif %} + {{ keyword }} +{% endfor %} +{% endmetablock %}{% endblock %} + +{% block meta_description %}{% metablock %} +{{ page.description }} +{% endmetablock %}{% endblock %} + +{% block title %} + {% editable page.title %} + {{ page.title }} + {% endeditable %} + + {% editable page.basicpage.sub_title %} + {{ page.basicpage.sub_title }} + {% endeditable %} +{% endblock %} + +{% block main %} + + {% editable page.basicpage.content %} + {{ page.basicpage.content|richtext_filters|safe }} + {% endeditable %} + +
    +
    +
    +
    + + {{ page.basicpage.photo }}
    + {{ page.basicpage.photo_credits }}
    + {{ page.basicpage.photo_alignment }}
    + {{ page.basicpage.photo_description }}
    + {{ page.basicpage.photo_featured }}
    + {{ page.basicpage.photo_featured_credits }}
    +{% endblock %} diff --git a/app/templates/pages/page.html b/app/templates/pages/page.html index 141c6013..b56657f4 100644 --- a/app/templates/pages/page.html +++ b/app/templates/pages/page.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n mezzanine_tags keyword_tags festival_tags %} +{% load i18n mezzanine_tags keyword_tags featured_tags %} {% block meta_title %}{{ page.meta_title }}{% endblock %} diff --git a/app/templates/styles.html b/app/templates/styles.html index 0d2f875f..f6f43a6e 100644 --- a/app/templates/styles.html +++ b/app/templates/styles.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n festival_tags %} +{% load i18n featured_tags %} {% block meta_title %}{% trans "Styles" %}{% endblock %} diff --git a/app/templates/templates/festival/artist_detail.html b/app/templates/templates/festival/artist_detail.html index 5e1087c2..95ca5cd9 100644 --- a/app/templates/templates/festival/artist_detail.html +++ b/app/templates/templates/festival/artist_detail.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load i18n %} -{% load mezzanine_tags keyword_tags festival_tags %} +{% load mezzanine_tags keyword_tags featured_tags %} {% block title %} {{ artist.name }} diff --git a/app/templates/templates/festival/video_list.html b/app/templates/templates/festival/video_list.html index dcb45e66..f951aa2a 100644 --- a/app/templates/templates/festival/video_list.html +++ b/app/templates/templates/festival/video_list.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load i18n %} -{% load mezzanine_tags keyword_tags festival_tags %} +{% load mezzanine_tags keyword_tags featured_tags %} {% block title %} {% trans "Videos" %} diff --git a/app/urls.py b/app/urls.py index f952c637..6e0a5d17 100644 --- a/app/urls.py +++ b/app/urls.py @@ -27,8 +27,11 @@ if settings.USE_MODELTRANSLATION: urlpatterns += [ - url(r'^festival/', include('festival.urls')), + # App urls + + url("^", include('organization.urls')), url("^%s/" % settings.EVENT_SLUG, include("mezzanine_agenda.urls")), + url("^styles/$", direct_to_template, {"template": "styles.html"}, name="styles"), # We don't want to presume how your homepage works, so here are a # few patterns you can use to set it up. @@ -40,8 +43,7 @@ urlpatterns += [ # one homepage pattern, so if you use a different one, comment this # one out. - url("^styles/$", direct_to_template, {"template": "styles.html"}, name="styles"), - url("^$", direct_to_template, {"template": "index.html"}, name="home"), + # url("^$", direct_to_template, {"template": "index.html"}, name="home"), # HOMEPAGE AS AN EDITABLE PAGE IN THE PAGE TREE # --------------------------------------------- diff --git a/doc/__init__.py b/doc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/requirements-dev.txt b/requirements-dev.txt index 327f10f4..381037a5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,6 @@ -e git+https://github.com/yomguy/mezzanine-agenda.git#egg=mezzanine-agenda-0.2.2 -e git+https://github.com/stephenmcd/mezzanine.git#egg=mezzanine-4.1-dev +-e git+https://github.com/stephenmcd/grappelli-safe#egg=grappelli-safe-0.4.2 #https://forge.ircam.fr/p/django-eve/source/download/dev/ # # diff --git a/requirements.txt b/requirements.txt index 8a9376ef..080247cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ django-meta django-bower django-debug-toolbar django-extensions +django-countries diff --git a/scripts/backup_db.sh b/scripts/backup_db.sh index 43b57a3f..35b7e2f5 100755 --- a/scripts/backup_db.sh +++ b/scripts/backup_db.sh @@ -1,4 +1,7 @@ #!/bin/bash -mysqldump -hdb -uroot -phyRob0otlaz4 ircam-www | gzip > /srv/backup/ircam-www.sql.gz +export PGPASSWORD=$POSTGRES_PASSWORD + +pg_dump -Fc -hdb -Upostgres -dpostgres > /srv/backup/ircam-www.dump + echo "Backup done!" diff --git a/scripts/makemigrations.sh b/scripts/makemigrations.sh new file mode 100755 index 00000000..e43f5cad --- /dev/null +++ b/scripts/makemigrations.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker-compose run app /srv/app/manage.py makemigrations diff --git a/scripts/migrate.sh b/scripts/migrate.sh new file mode 100755 index 00000000..8b06ce09 --- /dev/null +++ b/scripts/migrate.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker-compose run app /srv/app/manage.py migrate diff --git a/scripts/restore_db.sh b/scripts/restore_db.sh index 559caac4..ad8f18bf 100755 --- a/scripts/restore_db.sh +++ b/scripts/restore_db.sh @@ -1,4 +1,7 @@ #!/bin/bash -gunzip < /srv/backup/ircam-www.sql.gz | mysql -hdb -uroot -phyRob0otlaz4 ircam-www +export PGPASSWORD=$POSTGRES_PASSWORD + +pg_restore --clean -Fc -hdb -Upostgres -d postgres /srv/backup/ircam-www.dump + echo "Restore done!"