diff --git a/orchestra/apps/accounts/actions.py b/orchestra/apps/accounts/actions.py
index f6ee4286..037e1c66 100644
--- a/orchestra/apps/accounts/actions.py
+++ b/orchestra/apps/accounts/actions.py
@@ -1,5 +1,7 @@
from django.contrib import messages
+from django.core.urlresolvers import reverse
from django.db import transaction
+from django.shortcuts import redirect
from django.utils.translation import ungettext, ugettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation
@@ -20,3 +22,14 @@ def disable(modeladmin, request, queryset):
modeladmin.message_user(request, msg)
disable.url_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")
diff --git a/orchestra/apps/accounts/admin.py b/orchestra/apps/accounts/admin.py
index 3d0b3ef9..b4e7c196 100644
--- a/orchestra/apps/accounts/admin.py
+++ b/orchestra/apps/accounts/admin.py
@@ -18,7 +18,7 @@ from orchestra.core import services, accounts
from orchestra.forms import UserChangeForm
from . import settings
-from .actions import disable
+from .actions import disable, list_contacts
from .filters import HasMainUserListFilter
from .forms import AccountCreationForm
from .models import Account
@@ -61,8 +61,8 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
filter_horizontal = ()
change_readonly_fields = ('username', 'main_systemuser_link')
change_form_template = 'admin/accounts/account/change_form.html'
- actions = [disable]
- change_view_actions = actions
+ actions = [disable, list_contacts]
+ change_view_actions = [disable]
list_select_related = ('billcontact',)
ordering = ()
diff --git a/orchestra/apps/contacts/actions.py b/orchestra/apps/contacts/actions.py
new file mode 100644
index 00000000..315afab2
--- /dev/null
+++ b/orchestra/apps/contacts/actions.py
@@ -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)
diff --git a/orchestra/apps/contacts/admin.py b/orchestra/apps/contacts/admin.py
index eb1d2f9b..2ce161ef 100644
--- a/orchestra/apps/contacts/admin.py
+++ b/orchestra/apps/contacts/admin.py
@@ -2,22 +2,23 @@ from django import forms
from django.contrib import admin
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.apps.accounts.admin import AccountAdmin, AccountAdminMixin
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
+from .actions import SendEmail
from .models import Contact
-class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
+class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = (
'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link'
)
# TODO email usage custom filter contains
list_filter = ('email_usage',)
search_fields = (
- 'contact__account__name', 'short_name', 'full_name', 'phone', 'phone2',
+ 'account__username', 'account__full_name', 'short_name', 'full_name', 'phone', 'phone2',
'email'
)
fieldsets = (
@@ -38,6 +39,7 @@ class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
'fields': ('address', ('zipcode', 'city'), 'country')
}),
)
+ # TODO don't repeat all only for account_link do it on accountadmin
add_fieldsets = (
(None, {
'classes': ('wide',),
@@ -49,13 +51,14 @@ class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
}),
(_("Phone"), {
'classes': ('wide',),
- 'fields': ('phone', 'phone_alternative'),
+ 'fields': ('phone', 'phone2'),
}),
(_("Postal address"), {
'classes': ('wide',),
- 'fields': ('address', ('zip_code', 'city'), 'country')
+ 'fields': ('address', ('zipcode', 'city'), 'country')
}),
)
+ actions = [SendEmail(),]
def dispaly_name(self, contact):
return unicode(contact)
diff --git a/orchestra/apps/contacts/forms.py b/orchestra/apps/contacts/forms.py
new file mode 100644
index 00000000..fecf1a1e
--- /dev/null
+++ b/orchestra/apps/contacts/forms.py
@@ -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'])
diff --git a/orchestra/apps/contacts/settings.py b/orchestra/apps/contacts/settings.py
index 00ed01cb..737c8b48 100644
--- a/orchestra/apps/contacts/settings.py
+++ b/orchestra/apps/contacts/settings.py
@@ -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_FROM_EMAIL = getattr(settings, 'CONTACTS_DEFAULT_FROM_EMAIL', 'support@orchestra.lan')
diff --git a/orchestra/apps/orders/actions.py b/orchestra/apps/orders/actions.py
index 31a3a084..bec49b99 100644
--- a/orchestra/apps/orders/actions.py
+++ b/orchestra/apps/orders/actions.py
@@ -7,8 +7,7 @@ from django.shortcuts import render
from orchestra.admin.utils import change_url
-from .forms import (BillSelectedOptionsForm, BillSelectConfirmationForm,
- BillSelectRelatedForm)
+from .forms import BillSelectedOptionsForm, BillSelectConfirmationForm, BillSelectRelatedForm
class BillSelectedOrders(object):
diff --git a/orchestra/templates/admin/orchestra/generic_confirmation.html b/orchestra/templates/admin/orchestra/generic_confirmation.html
index acc04729..0b21e4d1 100644
--- a/orchestra/templates/admin/orchestra/generic_confirmation.html
+++ b/orchestra/templates/admin/orchestra/generic_confirmation.html
@@ -54,8 +54,8 @@
{% endfor %}
-
-
+
+
{% endblock %}