Added support for sending emails to contacts
This commit is contained in:
parent
4b15c742ff
commit
7382018f94
|
@ -1,5 +1,7 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin.decorators import action_with_confirmation
|
from orchestra.admin.decorators import action_with_confirmation
|
||||||
|
@ -20,3 +22,14 @@ def disable(modeladmin, request, queryset):
|
||||||
modeladmin.message_user(request, msg)
|
modeladmin.message_user(request, msg)
|
||||||
disable.url_name = 'disable'
|
disable.url_name = 'disable'
|
||||||
disable.verbose_name = _("Disable")
|
disable.verbose_name = _("Disable")
|
||||||
|
|
||||||
|
|
||||||
|
def list_contacts(modeladmin, request, queryset):
|
||||||
|
ids = queryset.values_list('id', flat=True)
|
||||||
|
if not ids:
|
||||||
|
message.warning(request, "Select at least one account.")
|
||||||
|
return
|
||||||
|
url = reverse('admin:contacts_contact_changelist')
|
||||||
|
url += '?account__in=%s' % ','.join(map(str, ids))
|
||||||
|
return redirect(url)
|
||||||
|
list_contacts.verbose_name = _("List contacts")
|
||||||
|
|
|
@ -18,7 +18,7 @@ from orchestra.core import services, accounts
|
||||||
from orchestra.forms import UserChangeForm
|
from orchestra.forms import UserChangeForm
|
||||||
|
|
||||||
from . import settings
|
from . import settings
|
||||||
from .actions import disable
|
from .actions import disable, list_contacts
|
||||||
from .filters import HasMainUserListFilter
|
from .filters import HasMainUserListFilter
|
||||||
from .forms import AccountCreationForm
|
from .forms import AccountCreationForm
|
||||||
from .models import Account
|
from .models import Account
|
||||||
|
@ -61,8 +61,8 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
filter_horizontal = ()
|
filter_horizontal = ()
|
||||||
change_readonly_fields = ('username', 'main_systemuser_link')
|
change_readonly_fields = ('username', 'main_systemuser_link')
|
||||||
change_form_template = 'admin/accounts/account/change_form.html'
|
change_form_template = 'admin/accounts/account/change_form.html'
|
||||||
actions = [disable]
|
actions = [disable, list_contacts]
|
||||||
change_view_actions = actions
|
change_view_actions = [disable]
|
||||||
list_select_related = ('billcontact',)
|
list_select_related = ('billcontact',)
|
||||||
ordering = ()
|
ordering = ()
|
||||||
|
|
||||||
|
|
96
orchestra/apps/contacts/actions.py
Normal file
96
orchestra/apps/contacts/actions.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
from django.contrib import admin, messages
|
||||||
|
from django.core.mail import send_mass_mail
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin.utils import change_url
|
||||||
|
|
||||||
|
from .forms import SendEmailForm
|
||||||
|
|
||||||
|
|
||||||
|
class SendEmail(object):
|
||||||
|
""" Form wizard for billing orders admin action """
|
||||||
|
short_description = _("Send email")
|
||||||
|
form = SendEmailForm
|
||||||
|
template = 'admin/orchestra/generic_confirmation.html'
|
||||||
|
__name__ = 'semd_email'
|
||||||
|
|
||||||
|
def __call__(self, modeladmin, request, queryset):
|
||||||
|
""" make this monster behave like a function """
|
||||||
|
self.modeladmin = modeladmin
|
||||||
|
self.queryset = queryset
|
||||||
|
opts = modeladmin.model._meta
|
||||||
|
app_label = opts.app_label
|
||||||
|
self.context = {
|
||||||
|
'action_name': _("Send email"),
|
||||||
|
'action_value': self.__name__,
|
||||||
|
'opts': opts,
|
||||||
|
'app_label': app_label,
|
||||||
|
'queryset': queryset,
|
||||||
|
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
|
||||||
|
}
|
||||||
|
return self.write_email(request)
|
||||||
|
|
||||||
|
def write_email(self, request):
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
raise PermissionDenied
|
||||||
|
form = self.form()
|
||||||
|
if request.POST.get('post'):
|
||||||
|
form = self.form(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
options = {
|
||||||
|
'email_from': form.cleaned_data['email_from'],
|
||||||
|
'cc': form.cleaned_data['cc'],
|
||||||
|
'bcc': form.cleaned_data['bcc'],
|
||||||
|
'subject': form.cleaned_data['subject'],
|
||||||
|
'message': form.cleaned_data['message'],
|
||||||
|
|
||||||
|
}
|
||||||
|
return self.confirm_email(request, **options)
|
||||||
|
opts = self.modeladmin.model._meta
|
||||||
|
app_label = opts.app_label
|
||||||
|
self.context.update({
|
||||||
|
'title': _("Send e-mail to contacts"),
|
||||||
|
'content_title': "",
|
||||||
|
'form': form,
|
||||||
|
'submit_value': _("Continue"),
|
||||||
|
})
|
||||||
|
# Display confirmation page
|
||||||
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
|
def confirm_email(self, request, **options):
|
||||||
|
num = len(self.queryset)
|
||||||
|
email_from = options['email_from']
|
||||||
|
bcc = options['bcc']
|
||||||
|
to = options['cc']
|
||||||
|
subject = options['subject']
|
||||||
|
message = options['message']
|
||||||
|
# The user has already confirmed
|
||||||
|
if request.POST.get('post') == 'email_confirmation':
|
||||||
|
for contact in self.queryset.all():
|
||||||
|
to.append(contact.email)
|
||||||
|
send_mass_mail(subject, message, email_from, to, bcc)
|
||||||
|
msg = ungettext(
|
||||||
|
_("Message has been sent to %s.") % str(contact),
|
||||||
|
_("Message has been sent to %i contacts.") % num,
|
||||||
|
num
|
||||||
|
)
|
||||||
|
self.modeladmin.message_user(request, msg)
|
||||||
|
return None
|
||||||
|
|
||||||
|
form = self.form(initial={
|
||||||
|
'subject': subject,
|
||||||
|
'message': message
|
||||||
|
})
|
||||||
|
self.context.update({
|
||||||
|
'title': _("Are you sure?"),
|
||||||
|
'content_message': _(
|
||||||
|
"Are you sure you want to send the following message to the following contacts?"),
|
||||||
|
'display_objects': ["%s (%s)" % (contact, contact.email) for contact in self.queryset],
|
||||||
|
'form': form,
|
||||||
|
'subject': subject,
|
||||||
|
'message': message,
|
||||||
|
'post_value': 'email_confirmation',
|
||||||
|
})
|
||||||
|
# Display the confirmation page
|
||||||
|
return render(request, self.template, self.context)
|
|
@ -2,22 +2,23 @@ from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import AtLeastOneRequiredInlineFormSet
|
from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import insertattr, admin_link, change_url
|
from orchestra.admin.utils import insertattr, admin_link, change_url
|
||||||
from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin
|
||||||
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
|
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
|
||||||
|
|
||||||
|
from .actions import SendEmail
|
||||||
from .models import Contact
|
from .models import Contact
|
||||||
|
|
||||||
|
|
||||||
class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
|
class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link'
|
'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link'
|
||||||
)
|
)
|
||||||
# TODO email usage custom filter contains
|
# TODO email usage custom filter contains
|
||||||
list_filter = ('email_usage',)
|
list_filter = ('email_usage',)
|
||||||
search_fields = (
|
search_fields = (
|
||||||
'contact__account__name', 'short_name', 'full_name', 'phone', 'phone2',
|
'account__username', 'account__full_name', 'short_name', 'full_name', 'phone', 'phone2',
|
||||||
'email'
|
'email'
|
||||||
)
|
)
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
@ -38,6 +39,7 @@ class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||||
'fields': ('address', ('zipcode', 'city'), 'country')
|
'fields': ('address', ('zipcode', 'city'), 'country')
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
# TODO don't repeat all only for account_link do it on accountadmin
|
||||||
add_fieldsets = (
|
add_fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
|
@ -49,13 +51,14 @@ class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||||
}),
|
}),
|
||||||
(_("Phone"), {
|
(_("Phone"), {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
'fields': ('phone', 'phone_alternative'),
|
'fields': ('phone', 'phone2'),
|
||||||
}),
|
}),
|
||||||
(_("Postal address"), {
|
(_("Postal address"), {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
'fields': ('address', ('zip_code', 'city'), 'country')
|
'fields': ('address', ('zipcode', 'city'), 'country')
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
actions = [SendEmail(),]
|
||||||
|
|
||||||
def dispaly_name(self, contact):
|
def dispaly_name(self, contact):
|
||||||
return unicode(contact)
|
return unicode(contact)
|
||||||
|
|
34
orchestra/apps/contacts/forms.py
Normal file
34
orchestra/apps/contacts/forms.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from django import forms
|
||||||
|
from django.core import validators
|
||||||
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
||||||
|
from . import settings
|
||||||
|
|
||||||
|
|
||||||
|
class SendEmailForm(forms.Form):
|
||||||
|
email_from = forms.EmailField(label=_("From"),
|
||||||
|
initial=settings.CONTACTS_DEFAULT_FROM_EMAIL,
|
||||||
|
widget=forms.TextInput(attrs={'size':'118'}))
|
||||||
|
cc = forms.CharField(label="CC", required=False,
|
||||||
|
widget=forms.TextInput(attrs={'size':'118'}))
|
||||||
|
bcc = forms.CharField(label="BCC", required=False,
|
||||||
|
widget=forms.TextInput(attrs={'size':'118'}))
|
||||||
|
subject = forms.CharField(label=_("Subject"),
|
||||||
|
widget=forms.TextInput(attrs={'size':'118'}))
|
||||||
|
message = forms.CharField(label=_("Message"),
|
||||||
|
widget=forms.Textarea(attrs={'cols': 118, 'rows': 15}))
|
||||||
|
|
||||||
|
def clean_space_separated_emails(self, value):
|
||||||
|
value = value.split()
|
||||||
|
for email in value:
|
||||||
|
try:
|
||||||
|
validators.validate_email(email)
|
||||||
|
except validators.ValidationError:
|
||||||
|
raise validators.ValidationError("Space separated emails.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
def clean_cc(self):
|
||||||
|
return self.clean_space_separated_emails(self.cleaned_data['cc'])
|
||||||
|
|
||||||
|
def clean_bcc(self):
|
||||||
|
return self.clean_space_separated_emails(self.cleaned_data['bcc'])
|
|
@ -14,3 +14,6 @@ CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', ((k,v) for k,v in d
|
||||||
|
|
||||||
|
|
||||||
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'ES')
|
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'ES')
|
||||||
|
|
||||||
|
|
||||||
|
CONTACTS_DEFAULT_FROM_EMAIL = getattr(settings, 'CONTACTS_DEFAULT_FROM_EMAIL', 'support@orchestra.lan')
|
||||||
|
|
|
@ -7,8 +7,7 @@ from django.shortcuts import render
|
||||||
|
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
|
|
||||||
from .forms import (BillSelectedOptionsForm, BillSelectConfirmationForm,
|
from .forms import BillSelectedOptionsForm, BillSelectConfirmationForm, BillSelectRelatedForm
|
||||||
BillSelectRelatedForm)
|
|
||||||
|
|
||||||
|
|
||||||
class BillSelectedOrders(object):
|
class BillSelectedOrders(object):
|
||||||
|
|
|
@ -54,8 +54,8 @@
|
||||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input type="hidden" name="action" value="{{ action_value }}" />
|
<input type="hidden" name="action" value="{{ action_value }}" />
|
||||||
<input type="hidden" name="post" value="generic_confirmation" />
|
<input type="hidden" name="post" value="{{ post_value|default:'generic_confirmation' }}" />
|
||||||
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
|
<input type="submit" value="{{ submit_value|default:_("Yes, I'm sure") }}" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in a new issue