diff --git a/TODO.md b/TODO.md
index a5515169..135d1726 100644
--- a/TODO.md
+++ b/TODO.md
@@ -430,11 +430,10 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# Automatically re-run backends until success? only timedout executions?
# TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects
-
# upgrade to django 1.9 and make margins wider
# lets encrypt: DNS vs HTTP challange
# lets enctypt: autorenew
-# lets encrypt: websites without / content
# Warning websites with ssl options without https protocol
+# Schedule cancellation
diff --git a/orchestra/contrib/accounts/admin.py b/orchestra/contrib/accounts/admin.py
index aa20c8ba..5abb40d7 100644
--- a/orchestra/contrib/accounts/admin.py
+++ b/orchestra/contrib/accounts/admin.py
@@ -202,10 +202,14 @@ class AccountAdminMixin(object):
except KeyError:
pass
else:
- help_text = (
- "Designates whether this account should be treated as active. "
- "Unselect this instead of deleting accounts."
- )
+ opts = self.model._meta
+ help_text = _(
+ "Designates whether this %(name)s should be treated as active. "
+ "Unselect this instead of deleting %(plural_name)s."
+ ) % {
+ 'name': opts.verbose_name,
+ 'plural_name': opts.verbose_name_plural,
+ }
if obj and not obj.account.is_active:
help_text += "
This user's account is dissabled"
field.help_text = _(help_text)
diff --git a/orchestra/contrib/domains/admin.py b/orchestra/contrib/domains/admin.py
index 33b187ef..25e799fa 100644
--- a/orchestra/contrib/domains/admin.py
+++ b/orchestra/contrib/domains/admin.py
@@ -95,14 +95,11 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
return '
'.join(links)
add_url = reverse('admin:websites_website_add')
add_url += '?account=%i&domains=%i' % (domain.account_id, domain.pk)
- context = {
- 'title': _("Add website"),
- 'url': add_url,
- 'image': '' % static('orchestra/images/add.png'),
- }
- add_link = '%(image)s' % context
- site_link = get_on_site_link('http://%s' % domain.name)
- return _("No website %s %s") % (add_link, site_link)
+ image = '' % static('orchestra/images/add.png')
+ add_link = '%s' % (
+ add_url, _("Add website"), image
+ )
+ return _("No website %s") % (add_link)
display_websites.admin_order_field = 'websites__name'
display_websites.short_description = _("Websites")
display_websites.allow_tags = True
diff --git a/orchestra/contrib/letsencrypt/actions.py b/orchestra/contrib/letsencrypt/actions.py
index d5672da8..a4d6ce8a 100644
--- a/orchestra/contrib/letsencrypt/actions.py
+++ b/orchestra/contrib/letsencrypt/actions.py
@@ -1,8 +1,9 @@
from django.contrib import messages, admin
from django.template.response import TemplateResponse
from django.utils.safestring import mark_safe
-from django.utils.translation import ungettext, ugettext_lazy as _
+from django.utils.translation import ungettext, ugettext, ugettext_lazy as _
+from orchestra.admin.utils import admin_link
from orchestra.contrib.orchestration import Operation, helpers
from .helpers import is_valid_domain, read_live_lineages, configure_cert
@@ -12,6 +13,18 @@ from .forms import LetsEncryptForm
def letsencrypt(modeladmin, request, queryset):
wildcards = set()
domains = set()
+ content_error = ''
+ contentless = queryset.exclude(content__path='/').distinct()
+ if contentless:
+ content_error = ungettext(
+ ugettext("Selected website %s doesn't have a webapp mounted on /."),
+ ugettext("Selected websites %s don't have a webapp mounted on /."),
+ len(contentless),
+ )
+ content_error += ugettext("
Websites need a webapp (e.g. static) mounted on / "
+ "for let's encrypt HTTP-01 challenge to work.")
+ content_error = content_error % ', '.join((admin_link()(website) for website in contentless))
+ content_error = '