add url support for speakerproposals and eventproposals, including new models Url and UrlType. Also switch to Django 2.0 path() syntax in various urls.py files getting rid of a lot of ugly regex \o/
This commit is contained in:
parent
df783168c6
commit
18c33383b7
|
@ -1,14 +1,14 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'backoffice'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', BackofficeIndexView.as_view(), name='index'),
|
||||
url(r'product_handout/$', ProductHandoutView.as_view(), name='product_handout'),
|
||||
url(r'badge_handout/$', BadgeHandoutView.as_view(), name='badge_handout'),
|
||||
url(r'ticket_checkin/$', TicketCheckinView.as_view(), name='ticket_checkin'),
|
||||
url(r'public_credit_names/$', ApproveNamesView.as_view(), name='public_credit_names'),
|
||||
path('', BackofficeIndexView.as_view(), name='index'),
|
||||
path('product_handout/', ProductHandoutView.as_view(), name='product_handout'),
|
||||
path('badge_handout/', BadgeHandoutView.as_view(), name='badge_handout'),
|
||||
path('ticket_checkin/', TicketCheckinView.as_view(), name='ticket_checkin'),
|
||||
path('public_credit_names/', ApproveNamesView.as_view(), name='public_credit_names'),
|
||||
]
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from allauth.account.views import (
|
|||
LogoutView,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
from camps.views import *
|
||||
from info.views import *
|
||||
|
@ -15,180 +15,180 @@ from people.views import *
|
|||
from bar.views import MenuView
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^profile/',
|
||||
path(
|
||||
'profile/',
|
||||
include('profiles.urls', namespace='profiles')
|
||||
),
|
||||
url(
|
||||
r'^tickets/',
|
||||
path(
|
||||
'tickets/',
|
||||
include('tickets.urls', namespace='tickets')
|
||||
),
|
||||
url(
|
||||
r'^shop/',
|
||||
path(
|
||||
'shop/',
|
||||
include('shop.urls', namespace='shop')
|
||||
),
|
||||
url(
|
||||
r'^news/',
|
||||
path(
|
||||
'news/',
|
||||
include('news.urls', namespace='news')
|
||||
),
|
||||
url(
|
||||
r'^contact/',
|
||||
path(
|
||||
'contact/',
|
||||
TemplateView.as_view(template_name='contact.html'),
|
||||
name='contact'
|
||||
),
|
||||
url(
|
||||
r'^conduct/',
|
||||
path(
|
||||
'conduct/',
|
||||
TemplateView.as_view(template_name='coc.html'),
|
||||
name='conduct'
|
||||
),
|
||||
url(
|
||||
r'^login/$',
|
||||
path(
|
||||
'login/',
|
||||
LoginView.as_view(),
|
||||
name='account_login',
|
||||
),
|
||||
url(
|
||||
r'^logout/$',
|
||||
path(
|
||||
'logout/',
|
||||
LogoutView.as_view(),
|
||||
name='account_logout',
|
||||
),
|
||||
url(
|
||||
r'^privacy-policy/$',
|
||||
path(
|
||||
'privacy-policy/',
|
||||
TemplateView.as_view(template_name='legal/privacy_policy.html'),
|
||||
name='privacy-policy'
|
||||
),
|
||||
url(
|
||||
r'^general-terms-and-conditions/$',
|
||||
path(
|
||||
'general-terms-and-conditions/',
|
||||
TemplateView.as_view(template_name='legal/general_terms_and_conditions.html'),
|
||||
name='general-terms'
|
||||
),
|
||||
url(r'^accounts/', include('allauth.urls')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
path('accounts/', include('allauth.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
url(
|
||||
r'^camps/$',
|
||||
path(
|
||||
'camps/',
|
||||
CampListView.as_view(),
|
||||
name='camp_list'
|
||||
),
|
||||
|
||||
# camp redirect views here
|
||||
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'',
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'camp_detail'},
|
||||
name='camp_detail_redirect',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^program/$',
|
||||
path(
|
||||
'program/',
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'schedule_index'},
|
||||
name='schedule_index_redirect',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^info/$',
|
||||
path(
|
||||
'info/',
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'info'},
|
||||
name='info_redirect',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^sponsors/$',
|
||||
path(
|
||||
'sponsors/',
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'sponsors'},
|
||||
name='sponsors_redirect',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^villages/$',
|
||||
path(
|
||||
'villages/',
|
||||
CampRedirectView.as_view(),
|
||||
kwargs={'page': 'village_list'},
|
||||
name='village_list_redirect',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^people/$',
|
||||
path(
|
||||
'people/',
|
||||
PeopleView.as_view(),
|
||||
name='people',
|
||||
),
|
||||
|
||||
url(
|
||||
r'^backoffice/',
|
||||
path(
|
||||
'backoffice/',
|
||||
include('backoffice.urls', namespace='backoffice')
|
||||
),
|
||||
|
||||
# camp specific urls below here
|
||||
|
||||
url(
|
||||
r'(?P<camp_slug>[-_\w+]+)/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'<slug:camp_slug>/', include([
|
||||
path(
|
||||
'',
|
||||
CampDetailView.as_view(),
|
||||
name='camp_detail'
|
||||
),
|
||||
|
||||
url(
|
||||
r'^info/$',
|
||||
path(
|
||||
'info/',
|
||||
CampInfoView.as_view(),
|
||||
name='info'
|
||||
),
|
||||
|
||||
url(
|
||||
r'^program/',
|
||||
path(
|
||||
'program/',
|
||||
include('program.urls', namespace='program'),
|
||||
),
|
||||
|
||||
url(
|
||||
r'^sponsors/call/$',
|
||||
path(
|
||||
'sponsors/call/',
|
||||
CallForSponsorsView.as_view(),
|
||||
name='call-for-sponsors'
|
||||
),
|
||||
url(
|
||||
r'^sponsors/$',
|
||||
path(
|
||||
'sponsors/',
|
||||
SponsorsView.as_view(),
|
||||
name='sponsors'
|
||||
),
|
||||
|
||||
url(
|
||||
r'^bar/menu$',
|
||||
path(
|
||||
'bar/menu',
|
||||
MenuView.as_view(),
|
||||
name='menu'
|
||||
),
|
||||
|
||||
url(
|
||||
r'^villages/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'villages/', include([
|
||||
path(
|
||||
'',
|
||||
VillageListView.as_view(),
|
||||
name='village_list'
|
||||
),
|
||||
url(
|
||||
r'create/$',
|
||||
path(
|
||||
'create/',
|
||||
VillageCreateView.as_view(),
|
||||
name='village_create'
|
||||
),
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/delete/$',
|
||||
path(
|
||||
'<slug:slug>/delete/',
|
||||
VillageDeleteView.as_view(),
|
||||
name='village_delete'
|
||||
),
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/edit/$',
|
||||
path(
|
||||
'<slug:slug>/edit/',
|
||||
VillageUpdateView.as_view(),
|
||||
name='village_update'
|
||||
),
|
||||
# this has to be the last url in the list
|
||||
url(
|
||||
r'(?P<slug>[-_\w+]+)/$',
|
||||
path(
|
||||
'<slug:slug>/',
|
||||
VillageDetailView.as_view(),
|
||||
name='village_detail'
|
||||
),
|
||||
])
|
||||
),
|
||||
|
||||
url(
|
||||
r'^teams/',
|
||||
path(
|
||||
'teams/',
|
||||
include('teams.urls', namespace='teams')
|
||||
),
|
||||
|
||||
|
@ -200,5 +200,6 @@ urlpatterns = [
|
|||
if settings.DEBUG:
|
||||
import debug_toolbar
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
path('__debug__/', include(debug_toolbar.urls)),
|
||||
] + urlpatterns
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'news'
|
||||
urlpatterns = [
|
||||
url(r'^$', views.NewsIndex.as_view(), kwargs={'archived': False}, name='index'),
|
||||
url(r'^archive/$', views.NewsIndex.as_view(), kwargs={'archived': True}, name='archive'),
|
||||
url(r'(?P<slug>[-_\w+]+)/$', views.NewsDetail.as_view(), name='detail'),
|
||||
path('', views.NewsIndex.as_view(), kwargs={'archived': False}, name='index'),
|
||||
path('archive/', views.NewsIndex.as_view(), kwargs={'archived': True}, name='archive'),
|
||||
path('<slug:slug>/', views.NewsDetail.as_view(), name='detail'),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from .views import ProfileDetail, ProfileUpdate
|
||||
|
||||
|
||||
app_name = 'profiles'
|
||||
urlpatterns = [
|
||||
url(r'^$', ProfileDetail.as_view(), name='detail'),
|
||||
url(r'^edit$', ProfileUpdate.as_view(), name='update'),
|
||||
path('', ProfileDetail.as_view(), name='detail'),
|
||||
path('edit', ProfileUpdate.as_view(), name='update'),
|
||||
]
|
||||
|
|
|
@ -14,7 +14,9 @@ from .models import (
|
|||
EventTrack,
|
||||
SpeakerProposal,
|
||||
EventProposal,
|
||||
Favorite
|
||||
Favorite,
|
||||
UrlType,
|
||||
Url
|
||||
)
|
||||
|
||||
|
||||
|
@ -98,3 +100,11 @@ class EventAdmin(admin.ModelAdmin):
|
|||
SpeakerInline
|
||||
]
|
||||
|
||||
@admin.register(UrlType)
|
||||
class UrlTypeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
@admin.register(Url)
|
||||
class UrlAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
|
48
src/program/migrations/0055_auto_20180521_2354.py
Normal file
48
src/program/migrations/0055_auto_20180521_2354.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
# Generated by Django 2.0.4 on 2018-05-21 21:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program', '0054_auto_20180520_1509'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Url',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('url', models.URLField(help_text='The actual URL')),
|
||||
('event', models.ForeignKey(blank=True, help_text='The event proposal object this URL belongs to', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='urls', to='program.Event')),
|
||||
('eventproposal', models.ForeignKey(blank=True, help_text='The event proposal object this URL belongs to', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='urls', to='program.EventProposal')),
|
||||
('speaker', models.ForeignKey(blank=True, help_text='The speaker proposal object this URL belongs to', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='urls', to='program.Speaker')),
|
||||
('speakerproposal', models.ForeignKey(blank=True, help_text='The speaker proposal object this URL belongs to', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='urls', to='program.SpeakerProposal')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UrlType',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('name', models.CharField(help_text='The name of this type', max_length=25)),
|
||||
('icon', models.CharField(help_text="Name of the fontawesome icon to use without the 'fa-' part", max_length=100)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='url',
|
||||
name='urltype',
|
||||
field=models.ForeignKey(help_text='The type of this URL', on_delete=django.db.models.deletion.PROTECT, to='program.UrlType'),
|
||||
),
|
||||
]
|
58
src/program/migrations/0056_add_urltypes.py
Normal file
58
src/program/migrations/0056_add_urltypes.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Generated by Django 2.0.4 on 2018-05-21 21:55
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def add_urltypes(apps, schema_editor):
|
||||
UrlType = apps.get_model('program', 'UrlType')
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Other',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Homepage',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Slides',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Twitter',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Mastodon',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Facebook',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Project',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
UrlType.objects.create(
|
||||
name='Blog',
|
||||
icon='link',
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program', '0055_auto_20180521_2354'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_urltypes),
|
||||
]
|
||||
|
18
src/program/migrations/0057_auto_20180522_0659.py
Normal file
18
src/program/migrations/0057_auto_20180522_0659.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.0.4 on 2018-05-22 04:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program', '0056_add_urltypes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='urltype',
|
||||
name='icon',
|
||||
field=models.CharField(default='link', help_text="Name of the fontawesome icon to use without the 'fa-' part", max_length=100),
|
||||
),
|
||||
]
|
22
src/program/migrations/0058_auto_20180523_0844.py
Normal file
22
src/program/migrations/0058_auto_20180523_0844.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 2.0.4 on 2018-05-23 06:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program', '0057_auto_20180522_0659'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='urltype',
|
||||
options={'ordering': ['name']},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='urltype',
|
||||
name='name',
|
||||
field=models.CharField(help_text='The name of this type', max_length=25, unique=True),
|
||||
),
|
||||
]
|
23
src/program/migrations/0059_auto_20180523_2241.py
Normal file
23
src/program/migrations/0059_auto_20180523_2241.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.0.4 on 2018-05-23 20:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program', '0058_auto_20180523_0844'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='url',
|
||||
name='id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='url',
|
||||
name='uuid',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
|
@ -1,11 +1,9 @@
|
|||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.shortcuts import redirect
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.urls import reverse
|
||||
from . import models
|
||||
from django.contrib import messages
|
||||
from django.http import Http404, HttpResponse
|
||||
import sys
|
||||
import mimetypes
|
||||
|
||||
|
||||
class EnsureCFPOpenMixin(object):
|
||||
|
@ -55,3 +53,42 @@ class EnsureUserOwnsProposalMixin(SingleObjectMixin):
|
|||
# alright, continue with the request
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UrlViewMixin(object):
|
||||
"""
|
||||
Mixin with code shared between all the Url views
|
||||
"""
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""
|
||||
Check that we have a valid SpeakerProposal or EventProposal and that it belongs to the current user
|
||||
"""
|
||||
# get the proposal
|
||||
if 'event_uuid' in self.kwargs:
|
||||
self.eventproposal = get_object_or_404(models.EventProposal, uuid=self.kwargs['event_uuid'], user=request.user)
|
||||
elif 'speaker_uuid' in self.kwargs:
|
||||
self.speakerproposal = get_object_or_404(models.SpeakerProposal, uuid=self.kwargs['speaker_uuid'], user=request.user)
|
||||
else:
|
||||
# fuckery afoot
|
||||
raise Http404
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Include the proposal in the template context
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
if hasattr(self, 'eventproposal') and self.eventproposal:
|
||||
context['eventproposal'] = self.eventproposal
|
||||
else:
|
||||
context['speakerproposal'] = self.speakerproposal
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
"""
|
||||
Return to the detail view of the proposal
|
||||
"""
|
||||
if hasattr(self, 'eventproposal'):
|
||||
return self.eventproposal.get_absolute_url()
|
||||
else:
|
||||
return self.speakerproposal.get_absolute_url()
|
||||
|
||||
|
|
|
@ -15,11 +15,141 @@ from django.core.files.storage import FileSystemStorage
|
|||
from django.urls import reverse
|
||||
from django.apps import apps
|
||||
from django.core.files.base import ContentFile
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from utils.models import CreatedUpdatedModel, CampRelatedModel
|
||||
|
||||
|
||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||
|
||||
|
||||
class UrlType(CreatedUpdatedModel):
|
||||
"""
|
||||
Each Url object has a type.
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=25,
|
||||
help_text='The name of this type',
|
||||
unique=True,
|
||||
)
|
||||
|
||||
icon = models.CharField(
|
||||
max_length=100,
|
||||
default='link',
|
||||
help_text="Name of the fontawesome icon to use without the 'fa-' part"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Url(CampRelatedModel):
|
||||
"""
|
||||
This model contains URLs related to
|
||||
- SpeakerProposals
|
||||
- EventProposals
|
||||
- Speakers
|
||||
- Events
|
||||
Each URL has a UrlType and a GenericForeignKey to the model to which it belongs.
|
||||
When a SpeakerProposal or EventProposal is approved the related URLs will be copied with FK to the new Speaker/Event objects.
|
||||
"""
|
||||
uuid = models.UUIDField(
|
||||
primary_key=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
)
|
||||
|
||||
url = models.URLField(
|
||||
help_text='The actual URL'
|
||||
)
|
||||
|
||||
urltype = models.ForeignKey(
|
||||
'program.UrlType',
|
||||
help_text='The type of this URL',
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
speakerproposal = models.ForeignKey(
|
||||
'program.SpeakerProposal',
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='The speaker proposal object this URL belongs to',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='urls',
|
||||
)
|
||||
|
||||
eventproposal = models.ForeignKey(
|
||||
'program.EventProposal',
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='The event proposal object this URL belongs to',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='urls',
|
||||
)
|
||||
|
||||
speaker = models.ForeignKey(
|
||||
'program.Speaker',
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='The speaker proposal object this URL belongs to',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='urls',
|
||||
)
|
||||
|
||||
event = models.ForeignKey(
|
||||
'program.Event',
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='The event proposal object this URL belongs to',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='urls',
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
||||
def clean(self):
|
||||
''' Make sure we have exactly one FK '''
|
||||
fks = 0
|
||||
if self.speakerproposal:
|
||||
fks += 1
|
||||
if self.eventproposal:
|
||||
fks += 1
|
||||
if self.speaker:
|
||||
fks += 1
|
||||
if self.event:
|
||||
fks += 1
|
||||
if fks > 1:
|
||||
raise(ValidationError("Url objects must have maximum one FK, this has %s" % fks))
|
||||
|
||||
@property
|
||||
def owner(self):
|
||||
"""
|
||||
Return the object this Url belongs to
|
||||
"""
|
||||
if self.speakerproposal:
|
||||
return self.speakerproposal
|
||||
elif self.eventproposal:
|
||||
return self.eventproposal
|
||||
elif self.speaker:
|
||||
return self.speaker
|
||||
elif self.event:
|
||||
return self.event
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def camp(self):
|
||||
return self.owner.camp
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
class UserSubmittedModel(CampRelatedModel):
|
||||
"""
|
||||
An abstract model containing the stuff that is shared
|
||||
|
|
|
@ -15,7 +15,19 @@
|
|||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Events</div>
|
||||
<div class="panel-heading">URLs for {{ eventproposal.title }}</div>
|
||||
<div class="panel-body">
|
||||
{% if eventproposal.urls.exists %}
|
||||
{% include 'includes/eventproposalurl_table.html' %}
|
||||
{% else %}
|
||||
<i>Nothing found.</i>
|
||||
{% endif %}
|
||||
<a href="{% url 'program:eventproposalurl_create' camp_slug=camp.slug event_uuid=eventproposal.uuid %}" class="btn btn-success btn-sm pull-right"><i class="fas fa-plus"></i><span class="h5"> Add URL</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{ eventproposal.event_type.host_title }} List</div>
|
||||
<div class="panel-body">
|
||||
{% if eventproposal.speakers.exists %}
|
||||
{% include 'includes/speaker_proposal_table.html' with speakerproposals=eventproposal.speakers.all %}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Type</th>
|
||||
<th>URLs</th>
|
||||
<th>People</th>
|
||||
<th>Track</th>
|
||||
<th>Status</th>
|
||||
|
@ -14,6 +15,7 @@
|
|||
<tr>
|
||||
<td><span class="h4">{{ eventproposal.title }}</span></td>
|
||||
<td><i class="fas fa-{{ eventproposal.event_type.icon }} fa-lg" style="color: {{ eventproposal.event_type.color }};"></i><span class="h4"> {{ eventproposal.event_type }}</span></td>
|
||||
<td><span class="h4">{% for url in eventproposal.urls.all %}<a href="{{ url.url }}"><i class="fas fa-{{ url.urltype.icon }}" data-toggle="tooltip" title="{{ url.urltype.name }}"></i></a> {% empty %}N/A{% endfor %}</span></td>
|
||||
<td><span class="h4">{% for person in eventproposal.speakers.all %}<a href="{% url 'program:speakerproposal_detail' camp_slug=camp.slug pk=person.uuid %}"><i class="fas fa-user" data-toggle="tooltip" title="{{ person.name }}"></i></a> {% endfor %}</span></td>
|
||||
<td><span class="h4">{{ eventproposal.track.name }}</span></td>
|
||||
<td><span class="badge">{{ eventproposal.proposal_status }}</span></td>
|
||||
|
@ -23,6 +25,7 @@
|
|||
<i class="fas fa-eye"></i><span class="h5"> Detail</span></a>
|
||||
{% if not camp.read_only %}
|
||||
<a href="{% url 'program:eventproposal_update' camp_slug=camp.slug pk=eventproposal.uuid %}" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i><span class="h5"> Modify</span></a>
|
||||
<a href="{% url 'program:eventproposalurl_create' camp_slug=camp.slug event_uuid=eventproposal.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-plus"></i><span class="h5"> Add URL</span></a>
|
||||
{% if eventproposal.get_available_speakerproposals.exists %}
|
||||
<a href="{% url 'program:eventproposal_selectperson' camp_slug=camp.slug event_uuid=eventproposal.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-plus"></i><span class="h5"> Add {{ eventproposal.event_type.host_title }}</span></a>
|
||||
{% else %}
|
||||
|
|
23
src/program/templates/includes/eventproposalurl_table.html
Normal file
23
src/program/templates/includes/eventproposalurl_table.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>URLs</th>
|
||||
<th class='text-right'>Available Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for url in eventproposal.urls.all %}
|
||||
<tr>
|
||||
<td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td>
|
||||
<td><span class="h4"><a href="{{ url.url }}">{{ url }}</a></span></td>
|
||||
<td class='text-right'>
|
||||
{% if not camp.read_only %}
|
||||
<a href="{% url 'program:eventproposalurl_update' camp_slug=camp.slug event_uuid=eventproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a>
|
||||
<a href="{% url 'program:eventproposalurl_delete' camp_slug=camp.slug event_uuid=eventproposal.uuid url_uuid=url.uuid %}" class="btn btn-danger btn-sm"><i class="fas fa-times"></i><span class="h5"> Delete</span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -3,6 +3,7 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="text-center">Events</th>
|
||||
<th class="text-center">URLs</th>
|
||||
<th>Status</th>
|
||||
<th class="text-right">Available Actions</th>
|
||||
</tr>
|
||||
|
@ -20,6 +21,13 @@
|
|||
N/A
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% for url in speakerproposal.urls.all %}
|
||||
<a href="{{ url.url }}" data-toggle="tooltip" title="{{ url.urltype }}"><i class="fas fa-{{ url.urltype.icon }}"></i></a>
|
||||
{% empty %}
|
||||
N/A
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td><span class="badge">{{ speakerproposal.proposal_status }}</span></td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'program:speakerproposal_detail' camp_slug=camp.slug pk=speakerproposal.uuid %}"
|
||||
|
@ -27,7 +35,7 @@
|
|||
<i class="fas fa-eye"></i><span class="h5"> Detail</span></a>
|
||||
{% if not camp.read_only %}
|
||||
<a href="{% url 'program:speakerproposal_update' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i><span class="h5"> Modify</span></a>
|
||||
<a href="{% url 'program:eventproposal_typeselect' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-plus"></i><span class="h5"> Add Event</span></a>
|
||||
<a href="{% url 'program:speakerproposalurl_create' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-plus"></i><span class="h5"> Add URL</span></a>
|
||||
{% if not speakerproposal.eventproposals.all %}
|
||||
<a href="{% url 'program:speakerproposal_delete' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-danger btn-sm"><i class="fas fa-times"></i><span class="h5"> Delete</span></a>
|
||||
{% endif %}
|
||||
|
|
23
src/program/templates/includes/speakerproposalurl_table.html
Normal file
23
src/program/templates/includes/speakerproposalurl_table.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>URLs</th>
|
||||
<th class='text-right'>Available Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for url in speakerproposal.urls.all %}
|
||||
<tr>
|
||||
<td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td>
|
||||
<td><span class="h4"><a href="{{ url.url }}">{{ url }}</a></span></td>
|
||||
<td class='text-right'>
|
||||
{% if not camp.read_only %}
|
||||
<a href="{% url 'program:speakerproposalurl_update' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a>
|
||||
<a href="{% url 'program:speakerproposalurl_delete' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid url_uuid=url.uuid %}" class="btn btn-danger btn-sm"><i class="fas fa-times"></i><span class="h5"> Delete</span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -15,7 +15,19 @@
|
|||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Events</div>
|
||||
<div class="panel-heading">URLs for {{ speakerproposal.name }}</div>
|
||||
<div class="panel-body">
|
||||
{% if speakerproposal.urls.exists %}
|
||||
{% include 'includes/speakerproposalurl_table.html' %}
|
||||
{% else %}
|
||||
<i>Nothing found.</i>
|
||||
{% endif %}
|
||||
<a href="{% url 'program:speakerproposalurl_create' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid %}" class="btn btn-success btn-sm pull-right"><i class="fas fa-plus"></i><span class="h5"> Add URL</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Events for {{ speakerproposal.name }}</div>
|
||||
<div class="panel-body">
|
||||
{% if speakerproposal.eventproposals.exists %}
|
||||
{% include 'includes/event_proposal_table.html' with eventproposals=speakerproposal.eventproposals.all %}
|
||||
|
|
19
src/program/templates/url_delete.html
Normal file
19
src/program/templates/url_delete.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends 'program_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block program_content %}
|
||||
<h3>Delete URL</h3>
|
||||
<p class="lead">Really delete this URL? This action cannot be undone.</p>
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_button "<i class='fas fa-times'></i> Delete" button_type="submit" button_class="btn-danger" %}
|
||||
{% if speakerproposal %}
|
||||
<a href="{% url 'program:speakerproposal_detail' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary">
|
||||
{% else %}
|
||||
<a href="{% url 'program:eventproposal_detail' camp_slug=camp.slug pk=eventproposal.uuid %}" class="btn btn-primary">
|
||||
{% endif %}
|
||||
<i class='fas fa-undo'></i> Cancel</a>
|
||||
</form>
|
||||
{% endblock program_content %}
|
||||
|
21
src/program/templates/url_form.html
Normal file
21
src/program/templates/url_form.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% extends 'program_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block program_content %}
|
||||
|
||||
<h3>
|
||||
{% if object %}
|
||||
Update URL
|
||||
{% else %}
|
||||
Add URL to {% if speakerproposal %}{{ speakerproposal.name }}{% else %}{{ eventproposal.title }}{% endif %}
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_button "Save URL" button_type="submit" button_class="btn-primary" %}
|
||||
</form>
|
||||
|
||||
{% endblock program_content %}
|
||||
|
|
@ -1,154 +1,184 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.urls import path, include
|
||||
from .views import *
|
||||
|
||||
app_name = 'program'
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'',
|
||||
ScheduleView.as_view(),
|
||||
name='schedule_index'
|
||||
),
|
||||
url(
|
||||
r'^noscript/$',
|
||||
path(
|
||||
'noscript/',
|
||||
NoScriptScheduleView.as_view(),
|
||||
name='noscript_schedule_index'
|
||||
),
|
||||
url(
|
||||
r'^ics/', ICSView.as_view(), name="ics_view"
|
||||
path(
|
||||
'ics/', ICSView.as_view(), name="ics_view"
|
||||
),
|
||||
url(
|
||||
r'^control/', ProgramControlCenter.as_view(), name="program_control_center"
|
||||
path(
|
||||
'control/', ProgramControlCenter.as_view(), name="program_control_center"
|
||||
),
|
||||
url(
|
||||
r'^proposals/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'proposals/', include([
|
||||
path(
|
||||
'',
|
||||
ProposalListView.as_view(),
|
||||
name='proposal_list',
|
||||
),
|
||||
url(
|
||||
r'^submit/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'submit/', include([
|
||||
path(
|
||||
'',
|
||||
CombinedProposalTypeSelectView.as_view(),
|
||||
name='proposal_combined_type_select',
|
||||
),
|
||||
url(
|
||||
r'^(?P<event_type_slug>[-_\w+]+)/$',
|
||||
path(
|
||||
'<slug:event_type_slug>/',
|
||||
CombinedProposalSubmitView.as_view(),
|
||||
name='proposal_combined_submit',
|
||||
),
|
||||
url(
|
||||
r'^(?P<event_type_slug>[-_\w+]+)/select_person/$',
|
||||
path(
|
||||
'<slug:event_type_slug>/select_person/',
|
||||
CombinedProposalPersonSelectView.as_view(),
|
||||
name='proposal_combined_person_select',
|
||||
),
|
||||
]),
|
||||
),
|
||||
url(
|
||||
r'^people/', include([
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/$',
|
||||
path(
|
||||
'people/', include([
|
||||
path(
|
||||
'<uuid:pk>/',
|
||||
SpeakerProposalDetailView.as_view(),
|
||||
name='speakerproposal_detail'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/update/$',
|
||||
path(
|
||||
'<uuid:pk>/update/',
|
||||
SpeakerProposalUpdateView.as_view(),
|
||||
name='speakerproposal_update'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/delete/$',
|
||||
path(
|
||||
'<uuid:pk>/delete/',
|
||||
SpeakerProposalDeleteView.as_view(),
|
||||
name='speakerproposal_delete'
|
||||
),
|
||||
url(
|
||||
r'^(?P<speaker_uuid>[a-f0-9-]+)/add_event/$',
|
||||
path(
|
||||
'<uuid:speaker_uuid>/add_event/',
|
||||
EventProposalTypeSelectView.as_view(),
|
||||
name='eventproposal_typeselect'
|
||||
),
|
||||
url(
|
||||
r'^(?P<speaker_uuid>[a-f0-9-]+)/add_event/(?P<event_type_slug>[-_\w+]+)/$',
|
||||
path(
|
||||
'<uuid:speaker_uuid>/add_event/<slug:event_type_slug>/',
|
||||
EventProposalCreateView.as_view(),
|
||||
name='eventproposal_create'
|
||||
),
|
||||
path(
|
||||
'<uuid:speaker_uuid>/add_url/',
|
||||
UrlCreateView.as_view(),
|
||||
name='speakerproposalurl_create'
|
||||
),
|
||||
path(
|
||||
'<uuid:speaker_uuid>/urls/<uuid:url_uuid>/update/',
|
||||
UrlUpdateView.as_view(),
|
||||
name='speakerproposalurl_update'
|
||||
),
|
||||
path(
|
||||
'<uuid:speaker_uuid>/urls/<uuid:url_uuid>/delete/',
|
||||
UrlDeleteView.as_view(),
|
||||
name='speakerproposalurl_delete'
|
||||
),
|
||||
])
|
||||
),
|
||||
url(
|
||||
r'^events/', include([
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/$',
|
||||
path(
|
||||
'events/', include([
|
||||
path(
|
||||
'<uuid:pk>/',
|
||||
EventProposalDetailView.as_view(),
|
||||
name='eventproposal_detail'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/edit/$',
|
||||
path(
|
||||
'<uuid:pk>/update/',
|
||||
EventProposalUpdateView.as_view(),
|
||||
name='eventproposal_update'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>[a-f0-9-]+)/delete/$',
|
||||
path(
|
||||
'<uuid:pk>/delete/',
|
||||
EventProposalDeleteView.as_view(),
|
||||
name='eventproposal_delete'
|
||||
),
|
||||
url(
|
||||
r'^(?P<event_uuid>[a-f0-9-]+)/add_person/$',
|
||||
path(
|
||||
'<uuid:event_uuid>/add_person/',
|
||||
EventProposalSelectPersonView.as_view(),
|
||||
name='eventproposal_selectperson'
|
||||
),
|
||||
url(
|
||||
r'^(?P<event_uuid>[a-f0-9-]+)/add_person/new/$',
|
||||
path(
|
||||
'<uuid:event_uuid>/add_person/new/',
|
||||
SpeakerProposalCreateView.as_view(),
|
||||
name='speakerproposal_create'
|
||||
),
|
||||
url(
|
||||
r'^(?P<event_uuid>[a-f0-9-]+)/add_person/(?P<speaker_uuid>[a-f0-9-]+)/$',
|
||||
path(
|
||||
'<uuid:event_uuid>/add_person/<uuid:speaker_uuid>/',
|
||||
EventProposalAddPersonView.as_view(),
|
||||
name='eventproposal_addperson'
|
||||
),
|
||||
path(
|
||||
'<uuid:event_uuid>/add_url/',
|
||||
UrlCreateView.as_view(),
|
||||
name='eventproposalurl_create'
|
||||
),
|
||||
path(
|
||||
'<uuid:event_uuid>/urls/<uuid:url_uuid>/update/',
|
||||
UrlUpdateView.as_view(),
|
||||
name='eventproposalurl_update'
|
||||
),
|
||||
path(
|
||||
'<uuid:event_uuid>/urls/<uuid:url_uuid>/delete/',
|
||||
UrlDeleteView.as_view(),
|
||||
name='eventproposalurl_delete'
|
||||
),
|
||||
])
|
||||
),
|
||||
])
|
||||
),
|
||||
url(
|
||||
r'^speakers/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'speakers/', include([
|
||||
path(
|
||||
'',
|
||||
SpeakerListView.as_view(),
|
||||
name='speaker_index'
|
||||
),
|
||||
url(
|
||||
r'^(?P<slug>[-_\w+]+)/$',
|
||||
path(
|
||||
'<slug:slug>/',
|
||||
SpeakerDetailView.as_view(),
|
||||
name='speaker_detail'
|
||||
),
|
||||
]),
|
||||
),
|
||||
url(
|
||||
r'^events/$',
|
||||
path(
|
||||
'events/',
|
||||
EventListView.as_view(),
|
||||
name='event_index'
|
||||
),
|
||||
# legacy CFS url kept on purpose to keep old links functional
|
||||
url(
|
||||
r'^call-for-speakers/$',
|
||||
path(
|
||||
'call-for-speakers/',
|
||||
CallForParticipationView.as_view(),
|
||||
name='call_for_speakers'
|
||||
),
|
||||
url(
|
||||
r'^call-for-participation/$',
|
||||
path(
|
||||
'call-for-participation/',
|
||||
CallForParticipationView.as_view(),
|
||||
name='call_for_participation'
|
||||
),
|
||||
url(
|
||||
r'^calendar/',
|
||||
path(
|
||||
'calendar',
|
||||
ICSView.as_view(),
|
||||
name='ics_calendar'
|
||||
),
|
||||
# this must be the last URL here or the regex will overrule the others
|
||||
url(
|
||||
r'^(?P<slug>[-_\w+]+)/$',
|
||||
path(
|
||||
'<slug:slug>',
|
||||
EventDetailView.as_view(),
|
||||
name='event_detail'
|
||||
),
|
||||
|
|
|
@ -24,7 +24,8 @@ from .mixins import (
|
|||
EnsureUnapprovedProposalMixin,
|
||||
EnsureUserOwnsProposalMixin,
|
||||
EnsureWritableCampMixin,
|
||||
EnsureCFPOpenMixin
|
||||
EnsureCFPOpenMixin,
|
||||
UrlViewMixin,
|
||||
)
|
||||
from .email import (
|
||||
add_speakerproposal_updated_email,
|
||||
|
@ -600,3 +601,40 @@ class ProgramControlCenter(CampViewMixin, TemplateView):
|
|||
|
||||
return context
|
||||
|
||||
###################################################################################################
|
||||
# URL views
|
||||
|
||||
class UrlCreateView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureCFPOpenMixin, UrlViewMixin, CreateView):
|
||||
model = models.Url
|
||||
template_name = 'url_form.html'
|
||||
fields = ['urltype', 'url']
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
Set the proposal FK before saving
|
||||
"""
|
||||
if hasattr(self, 'eventproposal') and self.eventproposal:
|
||||
form.instance.eventproposal = self.eventproposal
|
||||
url = form.save()
|
||||
else:
|
||||
form.instance.speakerproposal = self.speakerproposal
|
||||
url = form.save()
|
||||
|
||||
messages.success(self.request, "URL saved.")
|
||||
|
||||
# all good
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
|
||||
class UrlUpdateView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureCFPOpenMixin, UrlViewMixin, UpdateView):
|
||||
model = models.Url
|
||||
template_name = 'url_form.html'
|
||||
fields = ['urltype', 'url']
|
||||
pk_url_kwarg = 'url_uuid'
|
||||
|
||||
|
||||
class UrlDeleteView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureCFPOpenMixin, UrlViewMixin, DeleteView):
|
||||
model = models.Url
|
||||
template_name = 'url_delete.html'
|
||||
pk_url_kwarg = 'url_uuid'
|
||||
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, include
|
||||
from .views import *
|
||||
|
||||
app_name = 'shop'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', ShopIndexView.as_view(), name='index'),
|
||||
path('', ShopIndexView.as_view(), name='index'),
|
||||
|
||||
url(r'products/(?P<slug>[-_\w+]+)/$', ProductDetailView.as_view(), name='product_detail'),
|
||||
path('products/<slug:slug>/', ProductDetailView.as_view(), name='product_detail'),
|
||||
|
||||
url(r'orders/$', OrderListView.as_view(), name='order_list'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/$', OrderDetailView.as_view(), name='order_detail'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/invoice/$', DownloadInvoiceView.as_view(), name='download_invoice'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/mark_as_paid/$', OrderMarkAsPaidView.as_view(), name='mark_order_as_paid'),
|
||||
path('orders/', OrderListView.as_view(), name='order_list'),
|
||||
path('orders/<int:pk>/', include([
|
||||
path('', OrderDetailView.as_view(), name='order_detail'),
|
||||
path('invoice/', DownloadInvoiceView.as_view(), name='download_invoice'),
|
||||
path('mark_as_paid/', OrderMarkAsPaidView.as_view(), name='mark_order_as_paid'),
|
||||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/creditcard/$', EpayFormView.as_view(), name='epay_form'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/creditcard/callback/$',EpayCallbackView.as_view(), name='epay_callback'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/creditcard/thanks/$', EpayThanksView.as_view(), name='epay_thanks'),
|
||||
path('pay/creditcard/', EpayFormView.as_view(), name='epay_form'),
|
||||
path('pay/creditcard/callback/',EpayCallbackView.as_view(), name='epay_callback'),
|
||||
path('pay/creditcard/thanks/', EpayThanksView.as_view(), name='epay_thanks'),
|
||||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/blockchain/$', CoinifyRedirectView.as_view(), name='coinify_pay'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/blockchain/callback/$', CoinifyCallbackView.as_view(), name='coinify_callback'),
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/blockchain/thanks/$', CoinifyThanksView.as_view(), name='coinify_thanks'),
|
||||
path('pay/blockchain/', CoinifyRedirectView.as_view(), name='coinify_pay'),
|
||||
path('pay/blockchain/callback/', CoinifyCallbackView.as_view(), name='coinify_callback'),
|
||||
path('pay/blockchain/thanks/', CoinifyThanksView.as_view(), name='coinify_thanks'),
|
||||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/banktransfer/$', BankTransferView.as_view(), name='bank_transfer'),
|
||||
path('pay/banktransfer/', BankTransferView.as_view(), name='bank_transfer'),
|
||||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/cash/$', CashView.as_view(), name='cash'),
|
||||
|
||||
url(r'creditnotes/$', CreditNoteListView.as_view(), name='creditnote_list'),
|
||||
url(r'creditnotes/(?P<pk>[0-9]+)/pdf/$', DownloadCreditNoteView.as_view(), name='download_creditnote'),
|
||||
path('pay/cash/', CashView.as_view(), name='cash'),
|
||||
])),
|
||||
path('creditnotes/', CreditNoteListView.as_view(), name='creditnote_list'),
|
||||
path('creditnotes/<int:pk>/pdf/', DownloadCreditNoteView.as_view(), name='download_creditnote'),
|
||||
]
|
||||
|
|
|
@ -1,72 +1,72 @@
|
|||
from django.conf.urls import url, include
|
||||
from django.urls import path, include
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'teams'
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'',
|
||||
TeamListView.as_view(),
|
||||
name='list'
|
||||
),
|
||||
url(
|
||||
r'^members/', include([
|
||||
url(
|
||||
r'^(?P<pk>[0-9]+)/remove/$',
|
||||
path(
|
||||
'members/', include([
|
||||
path(
|
||||
'<int:pk>/remove/',
|
||||
TeamMemberRemoveView.as_view(),
|
||||
name='teammember_remove',
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>[0-9]+)/approve/$',
|
||||
path(
|
||||
'<int:pk>/approve/',
|
||||
TeamMemberApproveView.as_view(),
|
||||
name='teammember_approve',
|
||||
),
|
||||
]),
|
||||
),
|
||||
url(
|
||||
r'^(?P<team_slug>[-_\w+]+)/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'<slug:team_slug>/', include([
|
||||
path(
|
||||
'',
|
||||
TeamDetailView.as_view(),
|
||||
name='detail'
|
||||
),
|
||||
url(
|
||||
r'^join/$',
|
||||
path(
|
||||
'join/',
|
||||
TeamJoinView.as_view(),
|
||||
name='join'
|
||||
),
|
||||
url(
|
||||
r'^leave/$',
|
||||
path(
|
||||
'leave/',
|
||||
TeamLeaveView.as_view(),
|
||||
name='leave'
|
||||
),
|
||||
url(
|
||||
r'^manage/$',
|
||||
path(
|
||||
'manage/',
|
||||
TeamManageView.as_view(),
|
||||
name='manage'
|
||||
),
|
||||
url(
|
||||
r'^fix_irc_acl/$',
|
||||
path(
|
||||
'fix_irc_acl/',
|
||||
FixIrcAclView.as_view(),
|
||||
name='fix_irc_acl',
|
||||
),
|
||||
url(
|
||||
r'^tasks/', include([
|
||||
url(
|
||||
r'^create/$',
|
||||
path(
|
||||
'tasks/', include([
|
||||
path(
|
||||
'create/',
|
||||
TaskCreateView.as_view(),
|
||||
name='task_create',
|
||||
),
|
||||
url(
|
||||
r'^(?P<slug>[-_\w+]+)/', include([
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'<slug:slug>/', include([
|
||||
path(
|
||||
'',
|
||||
TaskDetailView.as_view(),
|
||||
name='task_detail',
|
||||
),
|
||||
url(
|
||||
r'^update/$',
|
||||
path(
|
||||
'update/',
|
||||
TaskUpdateView.as_view(),
|
||||
name='task_update',
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from .views import (
|
||||
ShopTicketListView,
|
||||
|
@ -9,18 +9,18 @@ from .views import (
|
|||
app_name = 'tickets'
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^$',
|
||||
path(
|
||||
'',
|
||||
ShopTicketListView.as_view(),
|
||||
name='shopticket_list'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/download/$',
|
||||
path(
|
||||
'<uuid:pk>/download/',
|
||||
ShopTicketDownloadView.as_view(),
|
||||
name='shopticket_download'
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/edit/$',
|
||||
path(
|
||||
'<uuid:pk>/edit/',
|
||||
ShopTicketDetailView.as_view(),
|
||||
name='shopticket_edit'
|
||||
),
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'villages'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', VillageListView.as_view(), name='list'),
|
||||
url(r'create/$', VillageCreateView.as_view(), name='create'),
|
||||
url(r'(?P<slug>[-_\w+]+)/delete/$', VillageDeleteView.as_view(), name='delete'),
|
||||
url(r'(?P<slug>[-_\w+]+)/edit/$', VillageUpdateView.as_view(), name='update'),
|
||||
url(r'(?P<slug>[-_\w+]+)/$', VillageDetailView.as_view(), name='detail'),
|
||||
path('', VillageListView.as_view(), name='list'),
|
||||
path('create/', VillageCreateView.as_view(), name='create'),
|
||||
path('<slug:slug>/delete/', VillageDeleteView.as_view(), name='delete'),
|
||||
path('<slug:slug>/edit/', VillageUpdateView.as_view(), name='update'),
|
||||
path('<slug:slug>/', VillageDetailView.as_view(), name='detail'),
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue