Compare commits

..

5 Commits

Author SHA1 Message Date
Nate Smith 31ea760f9d add template for ticket with email sending 2019-08-07 01:06:38 -05:00
Nate Smith bfbf37fceb WIP commit 2019-08-07 01:05:58 -05:00
Nate Smith e0caeefce2 good migration 2019-07-19 20:53:17 -05:00
Nate Smith 004353343f remove bad migration 2019-07-19 20:53:04 -05:00
Nate Smith 05d5898fa9 add EmailTemplate 2019-07-19 20:52:01 -05:00
14 changed files with 120 additions and 45 deletions

View File

@ -9,4 +9,4 @@ VENV=/town/venvs/ttadmin
source $VENV/bin/activate source $VENV/bin/activate
export DJANGO_SETTINGS_MODULE=settings_live export DJANGO_SETTINGS_MODULE=settings_live
cd $APP_ROOT cd $APP_ROOT
gunicorn -t120 -b0.0.0.0:$PORT ttadmin.wsgi gunicorn -b0.0.0.0:$PORT ttadmin.wsgi

View File

@ -15,9 +15,9 @@ setup(
'License :: OSI Approved :: Affero GNU General Public License v3 (AGPLv3)', 'License :: OSI Approved :: Affero GNU General Public License v3 (AGPLv3)',
], ],
packages=['ttadmin'], packages=['ttadmin'],
install_requires = ['Django==1.11.29', install_requires = ['Django==1.10.2',
'sshpubkeys==2.2.0', 'sshpubkeys==2.2.0',
'psycopg2-binary==2.8.5', 'psycopg2==2.7.6.1',
'gunicorn==19.6.0', 'gunicorn==19.6.0',
'Mastodon.py==1.4.5', 'Mastodon.py==1.4.5',
'tweepy==3.7.0'], 'tweepy==3.7.0'],

View File

@ -20,15 +20,15 @@
</head> </head>
<body> <body>
<h1>tilde.town guestbook</h1> <h1>tilde.town guestbook</h1>
<p><em>don't try to post urls. it won't work.</em></p>
<marquee>~*~*~*~*say hello*~*~*~*~</marquee> <marquee>~*~*~*~*say hello*~*~*~*~</marquee>
<form class="tilde" action="{% url 'guestbook:guestbook' %}" method="post"> <!-- <form class="tilde" action="{% url 'guestbook:guestbook' %}" method="post">
{% csrf_token %} {% csrf_token %}
<table> <table>
{{form.as_table}} {{form.as_table}}
</table> </table>
<input type="submit" value="sign" > <input type="submit" value="sign" >
</form> </form>
-->
{% for m in messages %} {% for m in messages %}
<p> <p>

View File

@ -1,5 +1,3 @@
import re
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
@ -7,8 +5,6 @@ from django.views.generic.edit import FormView
from .forms import GuestbookForm from .forms import GuestbookForm
from .models import GuestbookMessage from .models import GuestbookMessage
SUSPICIOUS_RE = re.compile(r'https?://')
class GuestbookView(FormView): class GuestbookView(FormView):
form_class = GuestbookForm form_class = GuestbookForm
@ -21,7 +17,5 @@ class GuestbookView(FormView):
def form_valid(self, form): def form_valid(self, form):
del form.cleaned_data['captcha'] del form.cleaned_data['captcha']
if SUSPICIOUS_RE.search(form.cleaned_data['msg']) != None:
return redirect('guestbook:guestbook')
t = GuestbookMessage.objects.create(**form.cleaned_data) t = GuestbookMessage.objects.create(**form.cleaned_data)
return redirect('guestbook:guestbook') return redirect('guestbook:guestbook')

View File

@ -1,6 +1,7 @@
from django.conf.urls import url
from django.contrib import admin from django.contrib import admin
from django.forms import ModelForm from django.forms import ModelForm
from .models import Ticket, Note from .models import Ticket, Note, EmailTemplate
class ImmutableNoteInline(admin.TabularInline): class ImmutableNoteInline(admin.TabularInline):
@ -23,6 +24,11 @@ class NewNoteInline(admin.StackedInline):
return queryset.none() return queryset.none()
@admin.register(EmailTemplate)
class EmailTemplateAdmin(admin.ModelAdmin):
fields = ('name', 'body')
@admin.register(Ticket) @admin.register(Ticket)
class TicketAdmin(admin.ModelAdmin): class TicketAdmin(admin.ModelAdmin):
inlines = [ImmutableNoteInline, NewNoteInline] inlines = [ImmutableNoteInline, NewNoteInline]
@ -31,6 +37,8 @@ class TicketAdmin(admin.ModelAdmin):
list_filter = ('issue_status', 'issue_type', 'assigned') list_filter = ('issue_status', 'issue_type', 'assigned')
fields = ('submitted', 'name', 'email', 'assigned', 'issue_status', 'issue_type', 'issue_text') fields = ('submitted', 'name', 'email', 'assigned', 'issue_status', 'issue_type', 'issue_text')
change_form_template = "admin/ticket_with_email.html"
def save_related(self, request, form, formsets, change): def save_related(self, request, form, formsets, change):
# THIS IS EXTREMELY BOOTLEG AND MAY BREAK IF MORE INLINES ARE ADDED TO THIS ADMIN. # THIS IS EXTREMELY BOOTLEG AND MAY BREAK IF MORE INLINES ARE ADDED TO THIS ADMIN.
for formset in formsets: for formset in formsets:
@ -42,3 +50,23 @@ class TicketAdmin(admin.ModelAdmin):
note_form.save(commit=False) note_form.save(commit=False)
note_form.save_m2m() note_form.save_m2m()
return super().save_related(request, form, formsets, change) return super().save_related(request, form, formsets, change)
def change_view(self, request, object_id, form_url='', extra_context=None):
if extra_context is None:
extra_context = {}
extra_context['email_templates'] = EmailTemplate.objects.all()
return super().change_view(request, object_id, form_url, extra_context=extra_context)
def get_urls(self):
urls = super().get_urls()
custom_urls = [
url(r'emails/send$', self.admin_site.admin_view(self.send_email_view)),
]
return urls + custom_urls
def send_email_view(self, request):
pass

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2019-07-20 01:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('help', '0008_auto_20190718_2223'),
]
operations = [
migrations.CreateModel(
name='EmailTemplate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('body', models.TextField()),
],
),
]

View File

@ -46,3 +46,11 @@ class Note(Model):
def __str__(self): def __str__(self):
return "admin note" return "admin note"
class EmailTemplate(Model):
name = CharField(blank=False, null=False, max_length=50)
body = TextField(blank=False, null=False)
def __str__(self):
return f"'{self.name}' email template"

View File

@ -0,0 +1,43 @@
{% extends 'admin/change_form.html' %}
{% block after_field_sets %}
<h2>Send an email</h2>
<table>
<tr>
<td>
<textarea id="send_email_body" rows="20" cols="60"></textarea>
</td>
<td>
<h3>Copy a template over?</h3>
<table>
<!-- loop over email_templates -->
{% for tmpl in email_templates %}
<tr>
<td>
{{ tmpl }}
{% endfor %}
<tr>
<td><button>copy</button></td>
<td>
<h4>lost key</h4>
<p>Hi!<br>to update your key, please respond to this message with your new pub key.</p>
</td>
</tr>
<tr>
<td><button>copy</button></td>
<td>
<h4>delete confirmation</h4>
<p>Hi,<br>this email is to confirm you would like your town account deleted. would you
like your public html files to remain accessible?</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<button>Send!</button>
</td>
</tr>
</table>
{% endblock %}

View File

@ -2,8 +2,8 @@ from django.conf.urls import url, include
from django.contrib import admin from django.contrib import admin
urlpatterns = [ urlpatterns = [
# url(r'^help/', include('help.urls')), url(r'^help/', include('help.urls')),
url(r'^users/', include('users.urls')), url(r'^users/', include('users.urls')),
# url(r'^guestbook/', include('guestbook.urls')), url(r'^guestbook/', include('guestbook.urls')),
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
] ]

View File

@ -11,7 +11,7 @@ class PubkeyInline(admin.TabularInline):
def bulk_accept(madmin, req, qs): def bulk_accept(madmin, req, qs):
for townie in qs: for townie in qs:
townie.state = Townie.ACCEPTED townie.state = 'accepted'
townie.save() townie.save()
post_users_to_social(qs) post_users_to_social(qs)
@ -19,7 +19,7 @@ bulk_accept.short_description = 'mark selected townies as accepted'
def bulk_reject(madmin, req, qs): def bulk_reject(madmin, req, qs):
for townie in qs: for townie in qs:
townie.state = Townie.REJECTED townie.state = 'rejected'
townie.save() townie.save()
bulk_reject.short_description = 'mark selected townies as rejected' bulk_reject.short_description = 'mark selected townies as rejected'

View File

@ -12,9 +12,9 @@ submission_throttle = {}
throttle_submission = throttler(submission_throttle) throttle_submission = throttler(submission_throttle)
USERNAME_RE = re.compile(r'^[a-z][a-z0-9_]+$') USERNAME_RE = re.compile(r'[a-z][a-z0-9_]+')
USERNAME_MIN_LENGTH = 2 USERNAME_MIN_LENGTH = 3
DISPLAY_NAME_RE = re.compile(r"^[a-zA-Z0-9_\-']+$") DISPLAY_NAME_RE = re.compile(r"[a-zA-Z0-9_\-']+")
DISPLAY_MIN_LENGTH = 2 DISPLAY_MIN_LENGTH = 2

View File

@ -19,7 +19,6 @@ SSH_TYPE_CHOICES = (
('ssh-rsa', 'ssh-rsa',), ('ssh-rsa', 'ssh-rsa',),
('ssh-dss', 'ssh-dss',), ('ssh-dss', 'ssh-dss',),
('ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp256'), ('ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp256'),
('ssh-ed25519', 'ssh-ed25519'),
) )
DEFAULT_INDEX_PATH = '/etc/skel/public_html/index.html' DEFAULT_INDEX_PATH = '/etc/skel/public_html/index.html'
@ -79,13 +78,6 @@ class Townie(User):
def home(self): def home(self):
return os.path.join('/home', self.username) return os.path.join('/home', self.username)
def generate_gift(self):
command = '/town/bin/generate_welcome_present.sh'
error = _guarded_run(['sudo', command, self.username])
if error:
logger.error(error)
return
def send_welcome_email(self): def send_welcome_email(self):
welcome_tmpl = get_template('users/welcome_email.txt') welcome_tmpl = get_template('users/welcome_email.txt')
context = { context = {
@ -254,15 +246,9 @@ def on_townie_pre_save(sender, instance, **kwargs):
# See if we need to create the user on disk. # See if we need to create the user on disk.
if existing.unreviewed and instance.accepted: if existing.unreviewed and instance.accepted:
logger.info('Creating user {} on disk.'.format(instance.username)) logger.info('Creating user {} on disk.'.format(instance.username))
try: instance.create_on_disk()
instance.create_on_disk() instance.send_welcome_email()
instance.write_authorized_keys() instance.write_authorized_keys()
except Exception as e:
logger.error('Failed syncing user {} to disk: {}'.format(instance.username, e))
else:
instance.send_welcome_email()
instance.generate_gift()
return return
else: else:
# This user state transition is currently undefined. In the future, we can check for things # This user state transition is currently undefined. In the future, we can check for things

View File

@ -27,7 +27,7 @@
<li><a href="https://tilde.town/~kc/blackout/">black out</a></li> <li><a href="https://tilde.town/~kc/blackout/">black out</a></li>
<li><a href="https://tilde.town/~subtransience/machinecode/index.html">the machine room</a></li> <li><a href="https://tilde.town/~subtransience/machinecode/index.html">the machine room</a></li>
</ul> </ul>
<h2>or a <a href="https://cgi.tilde.town/users/random">random page</a></h2> <h2>or a <a href="https://tilde.town/cgi/random">random page</a></h2>
</td> </td>
<td style="padding:1em"> <td style="padding:1em">
<a href="http://giphy.com/gifs/cyndipop-golden-girls-bea-arthur-ToMjGpK80QLT7KLWPLO"> <a href="http://giphy.com/gifs/cyndipop-golden-girls-bea-arthur-ToMjGpK80QLT7KLWPLO">

View File

@ -12,17 +12,10 @@ from django.views.generic.edit import FormView
from .forms import TownieForm from .forms import TownieForm
from .models import Townie, Pubkey from .models import Townie, Pubkey
SIGNUPS_ENABLED = True
class SignupView(FormView): class SignupView(FormView):
form_class = TownieForm form_class = TownieForm
template_name = 'users/signup.html' template_name = 'users/signup.html'
extra_context = {"signups_enabled": False}
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['signups_enabled'] = SIGNUPS_ENABLED
return ctx
@transaction.atomic @transaction.atomic
def form_valid(self, form): def form_valid(self, form):