diff --git a/doc/getting_started/plugins.rst b/doc/getting_started/plugins.rst new file mode 100644 index 000000000..072a9c3dc --- /dev/null +++ b/doc/getting_started/plugins.rst @@ -0,0 +1,524 @@ +############# +Using plugins +############# + +************************** +Enable or disable a plugin +************************** + +Modoboa provides an online panel to control plugins activation. You +will find it at *Modoboa > Extensions*. + +To activate a plugin, check the corresponding box and click on the +*Apply* button. + +To deactivate a plugin, uncheck the corresponding box and click on the +*Apply* button. + +**************** +Per-admin limits +**************** + +This plugin offers a way to define limits about how many objects +(aliases, mailboxes) a domain administrator can create. + +It also brings a new administrative role: ``Reseller``. A reseller is a domain +administrator that can also manipulate domains and assign permissions +to domain administrators. + +If you don't want to limit a particular object type, just set the +associated value to -1. + +Default limits applied to new administrators can be changed through +the *Modoboa > Parameters > Limits* page. + +***************************** +Postfix relay domains support +***************************** + +This plugin adds the support for relay domains using postfix. You can +use it when the MTA managed by Modoboa is not the final destination of +one or several domains. + +If activated, two new objects will be available from the *Domains* +listing page: *relay domain* and *relay domain alias*. + +The extension is compatible with the *amavis* and *limits* +ones. Resellers will be able to create both new objects. + +Replace by the name of the database you use.To tell Postfix this feature exists, you must generate two new map +files and then update your configuration. + +To generate the map files, run the following command:: + + $ modoboa-admin.py postfix_maps --categories relaydomains --dbtype + +Replace values between ``<>`` by yours. + +Edit the :file:`/etc/postfix/main.cf` file and copy the following +lines inside:: + + relay_domains = :/etc/postfix/sql-relaydomains.cf + transport_maps = + :/etc/postfix/sql-relaydomains-transport.cf + :/etc/postfix/sql-relaydomain-aliases-transport.cf + + smtpd_recipient_restrictions = + permit_mynetworks + reject_unauth_destination + check_recipient_access + :/etc/postfix/sql-relay-recipient-verification.cf + +Replace ```` by the name of the database you use. + +Reload postfix. + +.. _amavis_frontend: + +******************** +Amavisd-new frontend +******************** + +This plugin provides a simple management frontend for `amavisd-new +`_. The supported features are: + +* SQL quarantine management : available to administrators or users, + possibility to delete or release messages +* Per domain customization (using policies): specify how amavisd-new + will handle traffic + +.. note:: + + The per-domain policies feature only works for new + installations. Currently, you can't use modoboa with an existing + database (ie. with data in ``users`` and ``policies`` tables). + +.. note:: + + This plugin requires amavisd-new version **2.7.0** or higher. If + you're planning to use the :ref:`selfservice`, you'll need version + **2.8.0**. + +Quick Amavis setup +================== + +By default, amavis doesn't use a database. To configure this +behaviour, you first need to create a dedicated database. This step is a +bit manual since no *ready-to-use* SQL schema is provided by +amavis. The information is located inside README files, one for `MySQL +`_ and one for `PostgreSQL +`_. + +Then, you must tell amavis to use this database for lookups and +quarantined messages storing. Here is a working configuration sample:: + + @lookup_sql_dsn = + (['DBI::database=;host=;port=', '', ']']); + + @storage_sql_dsn = + (['DBI::database=;host=;port=', '', ']']); + + # PostgreSQL users NEED this parameter! + # MySQL users only need this parameter is email addresses are stored + # using the VARBINARY type. + $sql_allow_8bit_address = 1; + + $virus_quarantine_method = 'sql:'; + $spam_quarantine_method = 'sql:'; + $banned_files_quarantine_method = 'sql:'; + $bad_header_quarantine_method = 'sql:'; + + $virus_quarantine_to = 'virus-quarantine'; + $banned_quarantine_to = 'banned-quarantine'; + $bad_header_quarantine_to = 'bad-header-quarantine'; + $spam_quarantine_to = 'spam-quarantine'; + +Replace values between ``<>`` by yours. To know how to configure +amavis to allow quarantined messages release, read this :ref:`section +`. + +.. note:: + + Amavis configuration allows for separate lookup and storage + databases but Modoboa doesn't support it yet. + +Connect Modoboa and Amavis +========================== + +You must tell to Modoboa where it can find the amavis +database. Inside :file:`settings.py`, add a new connection to the +``DATABASES`` variable like this:: + + DATABASES = { + # Stuff before + # + "amavis": { + "ENGINE" : "", + "HOST" : "", + "NAME" : "", + "USER" : "", + "PASSWORD" : "" + } + } + +Replace values between ``<>`` with yours. + +Cleanup +------- + +Storing quarantined messages to a database can quickly become a +perfomance killer. Modoboa provides a simple script to periodically +purge the quarantine database. To use it, add the following line +inside root's crontab:: + + 0 0 * * * /manage.py qcleanup + # + # Or like this if you use a virtual environment: + # 0 0 * * * /manage.py qcleanup + +Replace ``modoboa_site`` with the path of your Modoboa instance. + +By default, messages older than 14 days are automatically purged. You +can modify this value by changing the ``MAX_MESSAGES_AGE`` parameter +in the online panel. + +.. _amavis_release: + +Release messages +================ + +To release messages, first take a look at `this page +`_. It +explains how to configure amavisd-new to listen somewhere for the +AM.PDP protocol. This protocol is used to send requests. + +Below is an example of a working configuration:: + + $interface_policy{'SOCK'} = 'AM.PDP-SOCK'; + $interface_policy{'9998'} = 'AM.PDP-INET'; + + $policy_bank{'AM.PDP-SOCK'} = { + protocol => 'AM.PDP', + auth_required_release => 0, + }; + $policy_bank{'AM.PDP-INET'} = { + protocol => 'AM.PDP', + inet_acl => [qw( 127.0.0.1 [::1] )], + }; + +Don't forget to update the ``inet_acl`` list if you plan to release from +the network. + +Once amavisd-new is configured, just tell Modoboa where it can find +the *release server* by modifying the following parameters in the +online panel: + ++--------------------+--------------------+------------------------+ +|Name |Description |Default value | ++====================+====================+========================+ +|Amavis connection |Mode used to access |unix | +|mode |the PDP server | | ++--------------------+--------------------+------------------------+ +|PDP server address |PDP server address |localhost | +| |(if inet mode) | | ++--------------------+--------------------+------------------------+ +|PDP server port |PDP server port (if | | +| |inet mode) 9998 | | ++--------------------+--------------------+------------------------+ +|PDP server socket |Path to the PDP |/var/amavis/amavisd.sock| +| |server socket (if | | +| |unix mode) | | ++--------------------+--------------------+------------------------+ + +Deferred release +---------------- + +By default, simple users are not allowed to release messages +themselves. They are only allowed to send release requests to +administrators. + +As administrators are not always available or logged into Modoboa, a +notification tool is available. It sends reminder e-mails to every +administrators or domain administrators. To use it, add the following +example line to root's crontab:: + + 0 12 * * * /manage.py amnotify --baseurl='' + # + # Or like this if you use a virtual environment: + # 0 12 * * * /manage.py amnotify --baseurl='' + +You are free to change the frequency. + +.. note:: + + If you want to let users release their messages alone (not + recommended), go to the admin panel. + +The following parameters are available to let you customize this +feature: + ++--------------------+--------------------+------------------------+ +|Name |Description |Default value | ++====================+====================+========================+ +|Check requests |Interval between two|30 | +|interval |release requests | | +| |checks | | ++--------------------+--------------------+------------------------+ +|Allow direct release|Allow users to |no | +| |directly release | | +| |their messages | | ++--------------------+--------------------+------------------------+ +|Notifications sender|The e-mail address |notification@modoboa.org| +| |used to send | | +| |notitications | | ++--------------------+--------------------+------------------------+ + +.. _selfservice: + +Self-service mode +================= + +The *self-service* mode lets users act on quarantined messages without +beeing authenticated. They can: + +* View messages +* Remove messages +* Release messages (or send release requests) + +To access a specific message, they only need the following information: + +* Message's unique identifier +* Message's secret identifier + +This information is controlled by *amavis*, which is in charge of +notifying users when new messages are put into quarantine. Each +notification (one per message) must embark a direct link containing +the required identifiers. + +To activate this feature, go the administration panel and set the +**Enable self-service mode** parameter to yes. + +The last step is to customize the notification messages amavis +sends. The most important is to embark a direct link. Take a look at +the `README.customize `_ file to +learn what you're allowed to do. + +Here is a link example:: + + http:///quarantine/%i/?rcpt=%R&secret_id=[:secret_id] + +.. _stats: + +******************** +Graphical statistics +******************** + +This plugin collects various statistics about emails traffic on your +server. It parses a log file to collect information, store it into RRD +files (see `rrdtool `_) and then +generates graphics in PNG format. + +To use it, go to the online parameters panel and adapt the following +ones to your environment: + ++--------------------+--------------------+--------------------------+ +|Name |Description |Default value | ++====================+====================+==========================+ +|Path to the log file|Path to log file |/var/log/mail.log | +| |used to collect | | +| |statistics | | ++--------------------+--------------------+--------------------------+ +|Directory to store |Path to directory |/tmp/modoboa | +|RRD files |where RRD files are | | +| |stored | | ++--------------------+--------------------+--------------------------+ +|Directory to store |Path to directory |/media/stats| +|PNG files |where PNG files are | | +| |stored | | ++--------------------+--------------------+--------------------------+ + +Make sure the directory that will contain RRD files exists. If not, +create it before going further. For example (according to the previous +parameters):: + + $ mkdir /tmp/modoboa + +To finish, you need to collect information periodically in order to +feed the RRD files. Add the following line into root's crontab:: + + */5 * * * * /manage.py logparser &> /dev/null + # + # Or like this if you use a virtual environment: + # 0/5 * * * * /manage.py logparser &> /dev/null + +Replace ```` with the path of your Modoboa instance. + +Graphics will be automatically created after each parsing. + +.. _postfix_ar: + +*************************** +Postifx auto-reply messages +*************************** + +This plugin let users define an auto-reply message (*vacation*). It is +based on Postfix capabilities. + +The user that executes the autoreply script needs to access +:file:`settings.py`. You must apply proper permissions on this file. For +example, if :file:`settings.py` belongs to ``www-data:www-data``, you can add +the ``vmail`` user to the ``www-data`` group and set the read permission +for the group. + +To make Postfix use this feature, you need to update your +configuration files as follows: + +``/etc/postfix/main.cf``:: + + transport_maps = :/etc/postfix/sql-autoreplies-transport.cf + virtual_alias_maps = :/etc/postfix/sql-aliases.cf + :/etc/postfix/sql-domain-aliases-mailboxes.cf, + :/etc/postfix/sql-autoreplies.cf, + :/etc/postfix/sql-catchall-aliases.cf + +.. note:: + + The order used to define alias maps is important, please respect it + +``/etc/postfix/master.cf``:: + + autoreply unix - n n - - pipe + flags= user=vmail: argv=python /manage.py autoreply $sender $mailbox + +Replace ```` by the name of the database you +use. ```` is the path of your Modoboa instance. + +Then, create the requested map files:: + + $ modoboa-admin.py postfix_maps mapfiles --categories autoreply + +`mapfiles` is the directory where the files will be stored. Answer the +few questions and you're done. + +.. note:: + + Auto-reply messages are just sent one time per sender for a + pre-defined time period. By default, this period is equal to 1 day + (86400s), you can adjust this value by modifying the **Automatic + reply timeout** parameter available in the online panel. + +************* +Sieve filters +************* + +This plugin let users create server-side message filters, using the +`sievelib module `_ (which +provides Sieve and ManageSieve clients). + +Two working modes are available: + +* A raw mode: you create filters using the Sieve language directly + (advanced users) +* An assisted mode: you create filters using an intuitive form + +To use this plugin, your hosting setup must include a *ManageSieve* +server and your local delivery agent must understand the *Sieve* +language. Don't panic, Dovecot supports both :-) (refer to +:ref:`dovecot` to know how to enable those features). + +.. note:: + The sieve filters plugin requires that the :ref:`webmail` plugin is + activated and configured. + +Go the online panel and modify the following parameters in order to +communicate with the *ManageSieve* server: + ++--------------------+--------------------+--------------------+ +|Name |Description |Default value | ++====================+====================+====================+ +|Server address |Address of your |127.0.0.1 | +| |MANAGESIEVE server | | ++--------------------+--------------------+--------------------+ +|Server port |Listening port of |4190 | +| |your MANAGESIEVE | | +| |server | | ++--------------------+--------------------+--------------------+ +|Connect using |Use the STARTTLS |no | +|STARTTLS |extension | | ++--------------------+--------------------+--------------------+ +|Authentication |Prefered |auto | +|mechanism |authentication | | +| |mechanism | | ++--------------------+--------------------+--------------------+ + +.. _webmail: + +******* +Webmail +******* + +Modoboa provides a simple webmail: + +* Browse, read and compose messages, attachments are supported +* HTML messages are supported +* `CKeditor `_ integration +* Manipulate mailboxes (create, move, remove) +* Quota display + +To use it, go to the online panel and modify the following parameters +to communicate with your *IMAP* server (under *IMAP settings*): + ++--------------------+--------------------+--------------------+ +|Name |Description |Default value | ++====================+====================+====================+ +|Server address |Address of your IMAP|127.0.0.1 | +| |server | | ++--------------------+--------------------+--------------------+ +|Use a secured |Use a secured |no | +|connection |connection to access| | +| |IMAP server | | ++--------------------+--------------------+--------------------+ +|Server port |Listening port of |143 | +| |your IMAP server | | ++--------------------+--------------------+--------------------+ + +Do the same to communicate with your SMTP server (under *SMTP settings*): + ++--------------------+--------------------+--------------------+ +|Name |Description |Default value | ++====================+====================+====================+ +|Server address |Address of your SMTP|127.0.0.1 | +| |server | | ++--------------------+--------------------+--------------------+ +|Secured connection |Use a secured |None | +|mode |connection to access| | +| |SMTP server | | ++--------------------+--------------------+--------------------+ +|Server port |Listening port of |25 | +| |your SMTP server | | ++--------------------+--------------------+--------------------+ +|Authentication |Server needs |no | +|required |authentication | | ++--------------------+--------------------+--------------------+ + +.. note:: + + The size of each attachment sent with messages is limited. You can + change the default value by modifying the **Maximum attachment + size** parameter. + +Using CKeditor +============== + +Modoboa supports CKeditor to compose HTML messages. To use it, first +download it from `the official website `_, then +extract the tarball:: + + $ cd + $ tar xzf /path/to/ckeditor/tarball.tag.gz -C sitestatic/js/ + +And you're done! + +Now, each user has the possibility to choose between CKeditor and the +raw text editor to compose their messages. (see *User > Settings > +Preferences > Webmail*) diff --git a/doc/getting_started/upgrade.rst b/doc/getting_started/upgrade.rst index dc0fb6bbe..25b5236ec 100644 --- a/doc/getting_started/upgrade.rst +++ b/doc/getting_started/upgrade.rst @@ -51,6 +51,19 @@ Then, follow the common procedure:: Eventually, restart the web server process according to the environment you did choose. See :ref:`webservers` for more details. +1.2.0: radicale extension, bootstrap3 and more +============================================== + +A new notification service let administrator know about new Modoboa +versions. To activate it, you need to: + +#. Add `'modoboa.core.context_processors.top_notifications'`` to the + ``TEMPLATE_CONTEXT_PROCESSORS`` variable + +#. Define the ``MODOBOA_API_URL``:: + + MODOBOA_API_URL = 'http://api.modoboa.org/1/' + 1.1.7: manual learning for SpamAssassin ======================================= diff --git a/modoboa/extensions/admin/forms/account.py b/modoboa/extensions/admin/forms/account.py index 6e6045652..b2b55eb33 100644 --- a/modoboa/extensions/admin/forms/account.py +++ b/modoboa/extensions/admin/forms/account.py @@ -130,6 +130,9 @@ class AccountFormGeneral(forms.ModelForm): class AccountFormMail(forms.Form, DynamicForm): + + """Form to handle mail part.""" + email = forms.EmailField(label=ugettext_lazy("E-mail"), required=False) quota = forms.IntegerField( label=ugettext_lazy("Quota"), @@ -253,7 +256,43 @@ class AccountFormMail(forms.Form, DynamicForm): self.mb.save() events.raiseEvent('MailboxModified', self.mb) + def _update_aliases(self, user): + """Update mailbox aliases.""" + aliases = [] + for name, value in self.cleaned_data.iteritems(): + if not name.startswith("aliases"): + continue + if value == "": + continue + aliases.append(value) + + for alias in self.mb.alias_set.all(): + if not alias.full_address in aliases: + if len(alias.get_recipients()) >= 2: + continue + alias.delete() + else: + aliases.remove(alias.full_address) + if not aliases: + return + events.raiseEvent( + "CanCreate", user, "mailbox_aliases", len(aliases) + ) + for alias in aliases: + local_part, domname = split_mailbox(alias) + try: + self.mb.alias_set.get(address=local_part, domain__name=domname) + except Alias.DoesNotExist: + pass + else: + continue + al = Alias(address=local_part, enabled=account.is_active) + al.domain = Domain.objects.get(name=domname) + al.save(int_rcpts=[self.mb]) + al.post_create(user) + def save(self, user, account): + """Save or update account mailbox.""" if self.cleaned_data["email"] == "": return None @@ -271,36 +310,7 @@ class AccountFormMail(forms.Form, DynamicForm): account.email = self.cleaned_data["email"] account.save() - aliases = [] - for name, value in self.cleaned_data.iteritems(): - if not name.startswith("aliases"): - continue - if value == "": - continue - aliases.append(value) - - for alias in self.mb.alias_set.all(): - if not alias.full_address in aliases: - if len(alias.get_recipients()) >= 2: - continue - alias.delete() - else: - aliases.remove(alias.full_address) - if aliases: - events.raiseEvent( - "CanCreate", user, "mailbox_aliases", len(aliases) - ) - for alias in aliases: - local_part, domname = split_mailbox(alias) - try: - self.mb.alias_set.get(address=local_part, domain__name=domname) - except Alias.DoesNotExist: - pass - else: - continue - al = Alias(address=local_part, enabled=account.is_active) - al.domain = Domain.objects.get(name=domname) - al.save(int_rcpts=[self.mb], creator=user) + self._update_aliases() return self.mb diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py index 3c5e7ab39..e9efb0c78 100644 --- a/modoboa/extensions/admin/forms/domain.py +++ b/modoboa/extensions/admin/forms/domain.py @@ -216,6 +216,7 @@ class DomainFormOptions(forms.Form): self.fields["create_aliases"].required = True def clean_dom_admin_username(self): + """Ensure admin username is an email address.""" if '@' in self.cleaned_data["dom_admin_username"]: raise forms.ValidationError(_("Invalid format")) return self.cleaned_data["dom_admin_username"] @@ -225,7 +226,8 @@ class DomainFormOptions(forms.Form): return if self.cleaned_data["create_dom_admin"] == "no": return - username = "%s@%s" % (self.cleaned_data["dom_admin_username"], domain.name) + username = "%s@%s" % ( + self.cleaned_data["dom_admin_username"], domain.name) try: da = User.objects.get(username=username) except User.DoesNotExist: @@ -238,15 +240,18 @@ class DomainFormOptions(forms.Form): da.save() da.set_role("DomainAdmins") da.post_create(user) - mb = Mailbox(address=self.cleaned_data["dom_admin_username"], domain=domain, - user=da, use_domain_quota=True) + mb = Mailbox( + address=self.cleaned_data["dom_admin_username"], domain=domain, + user=da, use_domain_quota=True + ) mb.set_quota(override_rules=user.has_perm("admin.change_domain")) mb.save(creator=user) if self.cleaned_data["create_aliases"] == "yes": events.raiseEvent("CanCreate", user, "mailbox_aliases") - al = Alias(address="postmaster", domain=domain, enabled=True) - al.save(int_rcpts=[mb], creator=user) + alias = Alias(address="postmaster", domain=domain, enabled=True) + alias.save(int_rcpts=[mb]) + alias.post_create(user) domain.add_admin(da) diff --git a/modoboa/extensions/admin/models/alias.py b/modoboa/extensions/admin/models/alias.py index 64d587b9f..cbc6879c9 100644 --- a/modoboa/extensions/admin/models/alias.py +++ b/modoboa/extensions/admin/models/alias.py @@ -93,6 +93,7 @@ class Alias(AdminObject): grant_access_to_object(admin, self) def save(self, *args, **kwargs): + """Custom save method.""" if 'ext_rcpts' in kwargs: self.extmboxes = ",".join(kwargs['ext_rcpts']) \ if kwargs['ext_rcpts'] else "" @@ -105,19 +106,19 @@ class Alias(AdminObject): super(Alias, self).save(*args, **kwargs) curaliases = self.aliases.all() curmboxes = self.mboxes.all() - for t in int_rcpts: - if isinstance(t, Alias): - if not t in curaliases: - self.aliases.add(t) + for rcpt in int_rcpts: + if isinstance(rcpt, Alias): + if not rcpt in curaliases: + self.aliases.add(rcpt) continue - if not t in curmboxes: - self.mboxes.add(t) - for t in curaliases: - if not t in int_rcpts: - self.aliases.remove(t) - for t in curmboxes: - if not t in int_rcpts: - self.mboxes.remove(t) + if not rcpt in curmboxes: + self.mboxes.add(rcpt) + for rcpt in curaliases: + if not rcpt in int_rcpts: + self.aliases.remove(rcpt) + for rcpt in curmboxes: + if not rcpt in int_rcpts: + self.mboxes.remove(rcpt) def get_recipients(self, with_external=True): """Return the recipients list. diff --git a/modoboa/extensions/admin/templates/admin/aliasform.html b/modoboa/extensions/admin/templates/admin/aliasform.html index 6cbc5b75f..5eef9092c 100644 --- a/modoboa/extensions/admin/templates/admin/aliasform.html +++ b/modoboa/extensions/admin/templates/admin/aliasform.html @@ -2,7 +2,7 @@ {% block formbody %} {% render_field form.email %} {% render_fields_group form "recipients" %} -{% if form.instance.domain.enabled %} +{% if not form.instance.pk or form.instance.domain.enabled %} {% render_field form.enabled %} {% else %} {% include "common/generic_field.html" with field=form.enabled label_width="col-sm-4" inline_message=_("Domain is disabled") extra_classes="info" %} diff --git a/modoboa/extensions/admin/templates/admin/mailform.html b/modoboa/extensions/admin/templates/admin/mailform.html index d00bf3359..4b7b6ec63 100644 --- a/modoboa/extensions/admin/templates/admin/mailform.html +++ b/modoboa/extensions/admin/templates/admin/mailform.html @@ -1,5 +1,5 @@ {% load form_tags %} {% render_field form.email %} -{% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" label_width="col-sm-4" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") %} +{% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" label_width="col-sm-4" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") activator_size="col-sm-5" %} {% render_extra_fields form %} {% render_fields_group form "aliases" %} diff --git a/modoboa/extensions/amavis/general_callbacks.py b/modoboa/extensions/amavis/general_callbacks.py index 34447df24..7bca1b8e1 100644 --- a/modoboa/extensions/amavis/general_callbacks.py +++ b/modoboa/extensions/amavis/general_callbacks.py @@ -95,7 +95,9 @@ def on_mailboxalias_created(user, alias): return else: email = alias.full_address - Users.objects.create(email=email, policy=policy, fullname=email) + Users.objects.create( + email=email, policy=policy, fullname=email, priority=7 + ) @events.observe("MailboxAliasDeleted") diff --git a/modoboa/extensions/amavis/templates/amavis/domain_content_filter.html b/modoboa/extensions/amavis/templates/amavis/domain_content_filter.html index b15a6390b..d669b7824 100644 --- a/modoboa/extensions/amavis/templates/amavis/domain_content_filter.html +++ b/modoboa/extensions/amavis/templates/amavis/domain_content_filter.html @@ -14,5 +14,5 @@ {% trans "or more throw spam message away" %} -{% render_field form.spam_subject_tag2 activator=True activator_label=_("default") %} +{% render_field form.spam_subject_tag2 activator=True activator_label=_("default") activator_size="col-sm-3" %} {% render_field form.bypass_banned_checks %} diff --git a/modoboa/extensions/amavis/templates/amavis/main_action_bar.html b/modoboa/extensions/amavis/templates/amavis/main_action_bar.html index 4c44c54c5..ccea6116b 100644 --- a/modoboa/extensions/amavis/templates/amavis/main_action_bar.html +++ b/modoboa/extensions/amavis/templates/amavis/main_action_bar.html @@ -1,7 +1,7 @@ {% load lib_tags i18n %}
-
+
- -
+ +
{% include "common/email_searchbar.html" with extraopts=extraopts %}
-
+
{% if activator %} -
+