From b94e668e15f6a7d3b924fed602c7b61847228136 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 14 Oct 2013 21:36:45 +0200 Subject: [PATCH 01/10] Documentation update. --- doc/integration/imap_and_smtp.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/integration/imap_and_smtp.rst b/doc/integration/imap_and_smtp.rst index a8df706ba..0a6e89a24 100644 --- a/doc/integration/imap_and_smtp.rst +++ b/doc/integration/imap_and_smtp.rst @@ -25,6 +25,8 @@ variable:: Then, edit the ``inbox`` namespace and add the following lines:: + inbox = yes + mailbox Drafts { auto = create special_use = \Drafts @@ -455,7 +457,7 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file smtpd_recipient_restrictions = ... - check_recipient_access :/etc/postfix/maps/sql-maintain.cf + check_recipient_access :/etc/postfix/sql-maintain.cf permit_mynetworks ... From dc688ecc39d1e0f31b96e1fbd152fb3b4b2130b6 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 00:14:56 +0200 Subject: [PATCH 02/10] fixed the path for core/commands/templates --- MANIFEST.in | 3 ++- modoboa/core/commands/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 38f115085..02a707cf3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include README.rst COPYING requirements.txt recursive-include modoboa *.html *.js *.css *.png *.po *.mo *.json *.gif recursive-include modoboa/core/templates *.tpl -recursive-include doc * \ No newline at end of file +recursive-include modoboa/core/commands/templates *.tpl +recursive-include doc * diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py index e7ef1ae9e..9b2b1c9f2 100644 --- a/modoboa/core/commands/__init__.py +++ b/modoboa/core/commands/__init__.py @@ -19,7 +19,7 @@ class Command(object): self._verbose = verbose if not settings.configured: settings.configure() - self._templates_dir = "%s/commands/templates" % os.path.dirname(__file__) + self._templates_dir = "%s/templates" % os.path.dirname(__file__) def _render_template(self, tplfile, env): fp = open(tplfile) From d8f39f700b00200a171c244c540a494c21be5499 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 01:05:00 +0200 Subject: [PATCH 03/10] added possibility to supply database settings and domain via command line args --- modoboa/core/commands/deploy.py | 100 +++++++++++++++++++++++--------- requirements.txt | 1 + 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 91c90341e..322decb85 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -4,6 +4,8 @@ import os import shutil import getpass import subprocess +import dj_database_url + from django.core import management from django.template import Context, Template from modoboa.lib.sysutils import exec_cmd @@ -11,12 +13,12 @@ from . import Command dbconn_tpl = """ '{{ conn_name }}': { - 'ENGINE': 'django.db.backends.{{ dbtype }}', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '{{ dbname }}', # Or path to database file if using sqlite3. - 'USER': '{{ username }}', # Not used with sqlite3. - 'PASSWORD': '{{ password }}', # Not used with sqlite3. - 'HOST': '{{ dbhost }}', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'mysql' %} + 'ENGINE': '{{ ENGINE }}', + 'NAME': '{{ NAME }}', # Or path to database file if using sqlite3. + 'USER': '{% if USER %}{{ USER }}{% endif %}', # Not used with sqlite3. + 'PASSWORD': '{% if PASSWORD %}{{ PASSWORD }}{% endif %}', # Not used with sqlite3. + 'HOST': '{% if HOST %}{{ HOST }}{% endif %}', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '{% if PORT %}{{ PORT }}{% endif %}', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'django.db.backends.mysql' %} 'OPTIONS' : { "init_command" : 'SET foreign_key_checks = 0;', },{% endif %} @@ -43,6 +45,16 @@ class DeployCommand(Command): '--collectstatic', action='store_true', default=False, help='Run django collectstatic command' ) + self._parser.add_argument( + '--dburl', type=str, nargs=1, default=None, + help='The database-url for your modoboa instance') + self._parser.add_argument( + '--amavis_dburl', type=str, nargs=1, default=None, + help='The database-url for your amavis instance') + self._parser.add_argument( + '--domain', type=str, nargs=1, default=None, + help='The domain under which you want to deploy modoboa') + def _exec_django_command(self, name, cwd, *args): """Run a django command for the freshly created project @@ -76,22 +88,28 @@ class DeployCommand(Command): """ print "Configuring database connection: %s" % name info = {'conn_name': name, - 'dbtype': raw_input('Database type (mysql, postgres or sqlite3): ')} - if info['dbtype'] not in ['mysql', 'postgres', 'sqlite3']: - info['dbtype'] = 'mysql' - if info['dbtype'] == 'postgres': - info['dbtype'] = 'postgresql_psycopg2' - if info['dbtype'] == 'sqlite3': - info['dbname'] = '%s.db' % name + 'ENGINE': raw_input('Database type (mysql, postgres or sqlite3): ')} + if info['ENGINE'] not in ['mysql', 'postgres', 'sqlite3']: + info['ENGINE'] = 'django.db.backends.mysql' + default_port = 3306 + if info['ENGINE'] == 'postgres': + info['ENGINE'] = 'django.db.backends.postgresql_psycopg2' + default_port = 5432 + if info['ENGINE'] == 'sqlite3': + info['ENGINE'] = 'django.db.backends.sqlite3' + info['NAME'] = '%s.db' % name else: - default_host = 'localhost' if info['dbtype'] == 'mysql' else '' - info['dbhost'] = \ - raw_input("Database host (default: '%s'): " % default_host) - if info['dbhost'] == '': - info['dbhost'] = default_host - info['dbname'] = raw_input('Database name: ') - info['username'] = raw_input('Username: ') - info['password'] = getpass.getpass('Password: ') + # No need to set HOST to localhost, since django will do this + # automatically if HOST is not supplied + info['HOST'] = raw_input("Database host (default: localhost )") + + info['PORT'] = raw_input("Database port: (default: '%s'): " % default_port) + #leave port setting empty, if default value is supplied and leave it to django + if info['PORT'] == default_port: + info['PORT'] = '' + info['NAME'] = raw_input('Database name: ') + info['USER'] = raw_input('Username: ') + info['PASSWORD'] = getpass.getpass('Password: ') return info def handle(self, parsed_args): @@ -109,13 +127,41 @@ class DeployCommand(Command): django14 = False t = Template(dbconn_tpl) - default_conn = t.render(Context(self.ask_db_info())) - amavis_conn = t.render(Context(self.ask_db_info('amavis'))) \ - if parsed_args.with_amavis else None - allowed_host = raw_input( - 'Under which domain do you want to deploy modoboa? ' - ) + if parsed_args.dburl: + info = dj_database_url.config(default=parsed_args.dburl[0]) + # In case the user fails to supply a valid database url, fallback to manual mode + if not info: + print "There was a problem with your database-url. \n" + info = self.ask_db_info() + #If we set this earlier, our fallback method will never be triggered + info['conn_name'] = 'default' + else: + info = self.ask_db_info() + + default_conn = t.render(Context(info)) + + if parsed_args.with_amavis: + if parsed_args.amavis_dburl: + amavis_info = dj_database_url.config(default=parsed_args.amavis_dburl[0]) + # In case the user fails to supply a valid database url, fallback to manual mode + if not amavis_info: + amavis_info = self.ask_db_info('amavis') + #If we set this earlier, our fallback method will never be triggered + amavis_info['conn_name'] = 'amavis' + else: + amavis_info = self.ask_db_info('amavis') + + amavis_conn = t.render(Context(amavis_info)) + else: + amavis_conn = None + + if parsed_args.domain: + allowed_host = parsed_args.domain[0] + else: + allowed_host = raw_input( + 'Under which domain do you want to deploy modoboa? ' + ) mod = __import__(parsed_args.name, globals(), locals(), ['settings']) tpl = self._render_template( diff --git a/requirements.txt b/requirements.txt index 6b74c0566..d7ba96b5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ django django-reversion +dj-database-url south lxml pycrypto From 999a78bf2e89f6cbd4ef442aaee0fea9af03d2a1 Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Thu, 17 Oct 2013 01:26:50 +0200 Subject: [PATCH 04/10] added silent installation / database_urls to the docs --- doc/getting_started/install.rst | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/doc/getting_started/install.rst b/doc/getting_started/install.rst index 63dd59bfb..8a5afff43 100644 --- a/doc/getting_started/install.rst +++ b/doc/getting_started/install.rst @@ -94,7 +94,7 @@ Deployment `modoboa-admin.py`, a command line tool, let you deploy a *ready-to-use* Modoboa site using only one instruction:: - $ modoboa-admin.py deploy modoboa_example --syncdb --collectstatic [--with-amavis] + $ modoboa-admin.py deploy modoboa_example --syncdb --collectstatic [--with-amavis] [--dburl database-url] [--amavis_dburl database-url] Just answer the few questions and you're done. You can now go to the :ref:`first_use` section. @@ -104,6 +104,31 @@ Just answer the few questions and you're done. You can now go to the The `--with-amavis` option must be set only if you intend to use the :ref:`amavis_frontend`. + +In case you need a **silent installation**, e.g. if you're using Salt-Stack. +It's possible to supply the database credentials as commandline arguments. + +.. note:: + + `--dburl database-url` for the modoboa database credentials + `--amavis_dburl database-url` for the amavis database credentials + + Your database-url should meet the following syntax: + ``scheme://[user:pass@][host:port]/dbname`` + + **or** + + ``sqlite:////full/path/to/your/database/file.sqlite`` + + Available `schemes` are: + * postgres + * postgresql + * postgis + * mysql + * mysql2 + * sqlite + + .. note:: If you plan to serve Modoboa using a URL prefix, you must change the From 9efd08438d13c0c3eb914aa790606d75580271dd Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Fri, 18 Oct 2013 11:39:26 +0200 Subject: [PATCH 05/10] added preliminary test i am new to this --- tests/install_from_scratch.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/install_from_scratch.py b/tests/install_from_scratch.py index 4d6800cec..4924cf03f 100644 --- a/tests/install_from_scratch.py +++ b/tests/install_from_scratch.py @@ -64,5 +64,38 @@ class DeployTest(unittest.TestCase): self.assertEqual(code, 0) +class SilentDeployTest(unittest.TestCase): + dbtype = "mysql" + dbhost = "localhost" + projname = "modoboa_silenttest" + dbuser = "root" + dbpassword = "toto" + dburl = "%s://%s:%s@%s/%s" % (self.dbtype, self.dbuser, self.dbpassword, self.dbhost, self.projname) + + def setUp(self): + cmd = "mysqladmin -u %s -p%s create %s" % (self.dbuser, self.dbpassword, self.projname) + code, out = runcmd(cmd) + self.assertEqual(code, 0) + self.workdir = tempfile.mkdtemp() + + def tearDown(self): + if hasattr(self, "workdir"): + shutil.rmtree(self.workdir) + child = pexpect.spawn("mysqladmin -u root -p%s drop %s" % (self.dbpassword, self.projname)) + child.expect("\[y/N\]") + child.sendline("y") + child.expect('Database "%s" dropped' % self.projname) + + def test(self): + timeout = 2 + cmd = "modoboa-admin.py deploy --syncdb --collectstatic %s --dburl --domain %s" % (self.projname, self.dburl, 'localhost') + child = pexpect.spawn(cmd, cwd=self.workdir) + fout = open('install_silently_from_scratch.log','w') + child.logfile = fout + child.wait() + fout.close() + self.assertEqual(child.exitstatus, 0) + + if __name__ == "__main__": unittest.main() From 17c198e26f9ce9e2843b108f640f3ae8f06fe2ab Mon Sep 17 00:00:00 2001 From: Simon Kern Date: Sat, 2 Nov 2013 12:54:50 +0100 Subject: [PATCH 06/10] fixed wrong driver for PostgreSQL in dovecot conf --- doc/integration/imap_and_smtp.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/integration/imap_and_smtp.rst b/doc/integration/imap_and_smtp.rst index 0a6e89a24..521371b0c 100644 --- a/doc/integration/imap_and_smtp.rst +++ b/doc/integration/imap_and_smtp.rst @@ -113,7 +113,7 @@ content inside:: # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext args = /etc/dovecot/dovecot-sql.conf.ext } - + userdb sql { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext @@ -151,7 +151,7 @@ PostgreSQL users :: - driver = postgresql + driver = pgsql connect = host= dbname= user= password= @@ -215,7 +215,7 @@ proper permissions:: unix_listener dict { mode = 0600 user = - #group = + #group = } } @@ -394,12 +394,12 @@ Finally, configure the ``sieve`` plugin by editing the :file:`conf.d/90-sieve.conf` file. Put the follwing caontent inside:: plugin { - # Location of the active script. When ManageSieve is used this is actually + # Location of the active script. When ManageSieve is used this is actually # a symlink pointing to the active script in the sieve storage directory. sieve = ~/.dovecot.sieve # - # The path to the directory where the personal Sieve scripts are stored. For + # The path to the directory where the personal Sieve scripts are stored. For # ManageSieve this is where the uploaded scripts are stored. sieve_dir = ~/sieve } @@ -442,12 +442,12 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file virtual_transport = dovecot dovecot_destination_recipient_limit = 1 - virtual_minimum_uid = + virtual_minimum_uid = virtual_gid_maps = static: virtual_uid_maps = static: virtual_mailbox_base = /var/vmail - relay_domains = + relay_domains = virtual_mailbox_domains = :/etc/postfix/sql-domains.cf virtual_alias_domains = :/etc/postfix/sql-domain-aliases.cf virtual_mailbox_maps = :/etc/postfix/sql-mailboxes.cf From 0029cdc00cd7d92af000194743fc1923da189ad8 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sat, 2 Nov 2013 19:26:50 +0100 Subject: [PATCH 07/10] Fixed silent installation and unit tests. fixes #489 --- modoboa/core/commands/deploy.py | 43 +++++++------ .../core/commands/templates/settings.py.tpl | 3 +- tests/install_from_scratch.py | 64 +++++-------------- 3 files changed, 41 insertions(+), 69 deletions(-) diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 322decb85..5d10ad69b 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -9,7 +9,7 @@ import dj_database_url from django.core import management from django.template import Context, Template from modoboa.lib.sysutils import exec_cmd -from . import Command +from modoboa.core.commands import Command dbconn_tpl = """ '{{ conn_name }}': { @@ -18,7 +18,7 @@ dbconn_tpl = """ 'USER': '{% if USER %}{{ USER }}{% endif %}', # Not used with sqlite3. 'PASSWORD': '{% if PASSWORD %}{{ PASSWORD }}{% endif %}', # Not used with sqlite3. 'HOST': '{% if HOST %}{{ HOST }}{% endif %}', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '{% if PORT %}{{ PORT }}{% endif %}', # Set to empty string for default. Not used with sqlite3.{% if dbtype == 'django.db.backends.mysql' %} + 'PORT': '{% if PORT %}{{ PORT }}{% endif %}', # Set to empty string for default. Not used with sqlite3.{% if ENGINE == 'django.db.backends.mysql' %} 'OPTIONS' : { "init_command" : 'SET foreign_key_checks = 0;', },{% endif %} @@ -90,26 +90,27 @@ class DeployCommand(Command): info = {'conn_name': name, 'ENGINE': raw_input('Database type (mysql, postgres or sqlite3): ')} if info['ENGINE'] not in ['mysql', 'postgres', 'sqlite3']: - info['ENGINE'] = 'django.db.backends.mysql' - default_port = 3306 - if info['ENGINE'] == 'postgres': - info['ENGINE'] = 'django.db.backends.postgresql_psycopg2' - default_port = 5432 + raise RuntimeError('Unsupported database engine') + if info['ENGINE'] == 'sqlite3': info['ENGINE'] = 'django.db.backends.sqlite3' info['NAME'] = '%s.db' % name + return info + if info['ENGINE'] == 'postgres': + info['ENGINE'] = 'django.db.backends.postgresql_psycopg2' + default_port = 5432 else: - # No need to set HOST to localhost, since django will do this - # automatically if HOST is not supplied - info['HOST'] = raw_input("Database host (default: localhost )") - - info['PORT'] = raw_input("Database port: (default: '%s'): " % default_port) - #leave port setting empty, if default value is supplied and leave it to django - if info['PORT'] == default_port: - info['PORT'] = '' - info['NAME'] = raw_input('Database name: ') - info['USER'] = raw_input('Username: ') - info['PASSWORD'] = getpass.getpass('Password: ') + info['ENGINE'] = 'django.db.backends.mysql' + default_port = 3306 + info['HOST'] = raw_input("Database host (default: 'localhost'): ") + info['PORT'] = raw_input("Database port (default: '%s'): " % default_port) + # leave port setting empty, if default value is supplied and + # leave it to django + if info['PORT'] == default_port: + info['PORT'] = '' + info['NAME'] = raw_input('Database name: ') + info['USER'] = raw_input('Username: ') + info['PASSWORD'] = getpass.getpass('Password: ') return info def handle(self, parsed_args): @@ -172,15 +173,15 @@ class DeployCommand(Command): 'allowed_host': allowed_host } ) - fp = open("%s/settings.py" % path, "w") - fp.write(tpl) - fp.close() + with open("%s/settings.py" % path, "w") as fp: + fp.write(tpl) shutil.copyfile( "%s/urls.py.tpl" % self._templates_dir, "%s/urls.py" % path ) os.mkdir("%s/media" % path) if parsed_args.syncdb: + os.unlink("%s/settings.pyc" % path) self._exec_django_command( "syncdb", parsed_args.name, '--noinput' ) diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index c46ddd6f7..f86ad1e39 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -159,7 +159,6 @@ MODOBOA_APPS = ( 'modoboa.extensions.webmail', 'modoboa.extensions.stats', 'modoboa.extensions.sievefilters', - 'modoboa.extensions.stats', {% if not amavis_conn %}#{% endif %}'modoboa.extensions.amavis', ) @@ -167,6 +166,8 @@ INSTALLED_APPS += MODOBOA_APPS AUTH_USER_MODEL = 'core.User' +SOUTH_TESTS_MIGRATE = False + #MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png") AUTHENTICATION_BACKENDS = ( diff --git a/tests/install_from_scratch.py b/tests/install_from_scratch.py index 4924cf03f..50e03947c 100644 --- a/tests/install_from_scratch.py +++ b/tests/install_from_scratch.py @@ -1,19 +1,11 @@ # coding: utf-8 import os -import sys -import subprocess import tempfile import pexpect import shutil import unittest - - -def runcmd(cmd, **kwargs): - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - shell=True, **kwargs) - output = p.communicate()[0] - return p.returncode, output +from modoboa.lib.sysutils import exec_cmd class DeployTest(unittest.TestCase): @@ -25,19 +17,23 @@ class DeployTest(unittest.TestCase): def setUp(self): cmd = "mysqladmin -u %s -p%s create %s" % (self.dbuser, self.dbpassword, self.projname) - code, out = runcmd(cmd) + code, out = exec_cmd(cmd) self.assertEqual(code, 0) self.workdir = tempfile.mkdtemp() def tearDown(self): + path = os.path.join(self.workdir, self.projname) + code, output = exec_cmd("python manage.py test admin", cwd=path) + self.assertEqual(code, 0) + if hasattr(self, "workdir"): - shutil.rmtree(self.workdir) + shutil.rmtree(self.workdir) child = pexpect.spawn("mysqladmin -u root -p%s drop %s" % (self.dbpassword, self.projname)) child.expect("\[y/N\]") child.sendline("y") child.expect('Database "%s" dropped' % self.projname) - def test(self): + def test_standard(self): timeout = 2 cmd = "modoboa-admin.py deploy --syncdb --collectstatic %s" % self.projname child = pexpect.spawn(cmd, cwd=self.workdir) @@ -47,6 +43,8 @@ class DeployTest(unittest.TestCase): child.sendline(self.dbtype) child.expect("Database host \(default: 'localhost'\):", timeout=timeout) child.sendline(self.dbhost) + child.expect("Database port \(default: '3306'\):", timeout=timeout) + child.sendline('3306') child.expect("Database name:", timeout=timeout) child.sendline(self.projname) child.expect("Username:", timeout=timeout) @@ -59,43 +57,15 @@ class DeployTest(unittest.TestCase): fout.close() self.assertEqual(child.exitstatus, 0) - path = os.path.join(self.workdir, self.projname) - code, output = runcmd("python manage.py test admin", cwd=path) + def test_silent(self): + dburl = "%s://%s:%s@%s/%s" \ + % (self.dbtype, self.dbuser, self.dbpassword, + self.dbhost, self.projname) + cmd = "modoboa-admin.py deploy --syncdb --collectstatic --dburl %s --domain %s %s" \ + % (dburl, 'localhost', self.projname) + code, output = exec_cmd(cmd, cwd=self.workdir) self.assertEqual(code, 0) -class SilentDeployTest(unittest.TestCase): - dbtype = "mysql" - dbhost = "localhost" - projname = "modoboa_silenttest" - dbuser = "root" - dbpassword = "toto" - dburl = "%s://%s:%s@%s/%s" % (self.dbtype, self.dbuser, self.dbpassword, self.dbhost, self.projname) - - def setUp(self): - cmd = "mysqladmin -u %s -p%s create %s" % (self.dbuser, self.dbpassword, self.projname) - code, out = runcmd(cmd) - self.assertEqual(code, 0) - self.workdir = tempfile.mkdtemp() - - def tearDown(self): - if hasattr(self, "workdir"): - shutil.rmtree(self.workdir) - child = pexpect.spawn("mysqladmin -u root -p%s drop %s" % (self.dbpassword, self.projname)) - child.expect("\[y/N\]") - child.sendline("y") - child.expect('Database "%s" dropped' % self.projname) - - def test(self): - timeout = 2 - cmd = "modoboa-admin.py deploy --syncdb --collectstatic %s --dburl --domain %s" % (self.projname, self.dburl, 'localhost') - child = pexpect.spawn(cmd, cwd=self.workdir) - fout = open('install_silently_from_scratch.log','w') - child.logfile = fout - child.wait() - fout.close() - self.assertEqual(child.exitstatus, 0) - - if __name__ == "__main__": unittest.main() From c9103bd0e03e936e74b22293bfc129e76e068e90 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 4 Nov 2013 09:29:59 +0100 Subject: [PATCH 08/10] I forgot those modifications from 1.0.1 :p --- modoboa/core/__init__.py | 2 +- modoboa/core/app_settings.py | 13 ++++++++++--- modoboa/core/views/auth.py | 2 +- modoboa/lib/fixtures/demo.json | 2 +- modoboa/lib/webutils.py | 16 +++++----------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/modoboa/core/__init__.py b/modoboa/core/__init__.py index 824c49add..544b3a1b3 100644 --- a/modoboa/core/__init__.py +++ b/modoboa/core/__init__.py @@ -52,4 +52,4 @@ def unset_default_topredirection(extension): """ topredirection = parameters.get_admin("DEFAULT_TOP_REDIRECTION") if topredirection == extension.name: - parameters.save_admin("DEFAULT_TOP_REDIRECTION", "userprefs") + parameters.save_admin("DEFAULT_TOP_REDIRECTION", "core") diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index 9acfafae5..543672102 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -19,7 +19,7 @@ def enabled_applications(): from modoboa.core.models import Extension from modoboa.lib.dbutils import db_table_exists - result = [("core", "core")] + result = [("user", "user")] if db_table_exists("core_extension"): exts = Extension.objects.filter(enabled=True) result += [(ext.name, ext.name) for ext in exts] @@ -98,7 +98,7 @@ class GeneralParametersForm(parameters.AdminParametersForm): label=ugettext_lazy("Bind password"), initial='', help_text=ugettext_lazy("The password to use when binding to the LDAP server (with 'Bind DN')"), - widget=forms.PasswordInput, + widget=forms.PasswordInput(render_value=True), required=False ) @@ -152,7 +152,7 @@ class GeneralParametersForm(parameters.AdminParametersForm): default_top_redirection = forms.ChoiceField( label=ugettext_lazy("Default top redirection"), choices=[], - initial="core", + initial="user", help_text=ugettext_lazy("The default redirection used when no application is specified") ) @@ -176,6 +176,13 @@ class GeneralParametersForm(parameters.AdminParametersForm): super(GeneralParametersForm, self).__init__(*args, **kwargs) self.fields["default_top_redirection"].choices = enabled_applications() + def clean_secret_key(self): + if len(self.cleaned_data["secret_key"]) not in [16, 24, 32]: + raise forms.ValidationError( + _("Key must be either 16, 24, or 32 bytes long") + ) + return self.cleaned_data["secret_key"] + def clean_ldap_user_dn_template(self): tpl = self.cleaned_data["ldap_user_dn_template"] try: diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index f4d132d9e..67eb3f973 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -35,7 +35,7 @@ def dologin(request): nextlocation = request.POST.get("next", None) if nextlocation is None or nextlocation == "None": - if user.group != "SimpleUsers": + if user.group == "SimpleUsers": nextlocation = reverse("modoboa.lib.webutils.topredirection") else: nextlocation = reverse("domains") diff --git a/modoboa/lib/fixtures/demo.json b/modoboa/lib/fixtures/demo.json index d155e29d7..046dd26f0 100644 --- a/modoboa/lib/fixtures/demo.json +++ b/modoboa/lib/fixtures/demo.json @@ -20,7 +20,7 @@ "model": "lib.parameter", "fields": { "name": "general.DEFAULT_TOP_REDIRECTION", - "value": "userprefs" + "value": "user" } }, { diff --git a/modoboa/lib/webutils.py b/modoboa/lib/webutils.py index aa47ea23e..f85c90ecb 100644 --- a/modoboa/lib/webutils.py +++ b/modoboa/lib/webutils.py @@ -138,27 +138,21 @@ def size2integer(value): @login_required def topredirection(request): - """Simple view to redirect the request when no application is specified + """Simple view to redirect the request when no application is specified. The default "top redirection" can be specified in the *Admin > Settings* panel. It is the application that will be launched by default. Users that are not allowed to access this application will be redirected to the "User preferences" application. + This feature only applies to simple users. + :param request: a Request object """ from modoboa.lib import parameters from modoboa.core.extensions import exts_pool topredir = parameters.get_admin("DEFAULT_TOP_REDIRECTION", app="core") - if not topredir in ["core"]: - infos = exts_pool.get_extension_infos(topredir) - path = infos["url"] if infos["url"] else infos["name"] - else: - path = "admin" # topredir - - if topredir in ["core", "stats"] and \ - request.user.belongs_to_group('SimpleUsers'): - path = "userprefs" - + infos = exts_pool.get_extension_infos(topredir) + path = infos["url"] if infos["url"] else infos["name"] return HttpResponseRedirect(path) From 91ce9c5f5cd96b8919817b6d65196005aedb41a3 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 4 Nov 2013 10:27:28 +0100 Subject: [PATCH 09/10] Don't propose all extensions for top redirection. --- modoboa/core/app_settings.py | 6 +++++- modoboa/core/extensions.py | 1 + modoboa/extensions/amavis/__init__.py | 1 + modoboa/extensions/sievefilters/__init__.py | 1 + modoboa/extensions/webmail/__init__.py | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index 543672102..edb9bfe48 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -17,12 +17,16 @@ def enabled_applications(): :return: a list """ from modoboa.core.models import Extension + from modoboa.core.extensions import exts_pool from modoboa.lib.dbutils import db_table_exists result = [("user", "user")] if db_table_exists("core_extension"): exts = Extension.objects.filter(enabled=True) - result += [(ext.name, ext.name) for ext in exts] + for ext in exts: + extclass = exts_pool.get_extension(ext.name) + if extclass.available_for_topredirection: + result.append((ext.name, ext.name)) return sorted(result, key=lambda e: e[0]) diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index 4b399bd24..1eedc56a8 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -15,6 +15,7 @@ class ModoExtension(object): url = None needs_media = False always_active = False + available_for_topredirection = False def infos(self): return dict( diff --git a/modoboa/extensions/amavis/__init__.py b/modoboa/extensions/amavis/__init__.py index 9abc912ed..8a85d646c 100644 --- a/modoboa/extensions/amavis/__init__.py +++ b/modoboa/extensions/amavis/__init__.py @@ -21,6 +21,7 @@ class Amavis(ModoExtension): version = "1.0" description = ugettext_lazy("Simple amavis management frontend") url = "quarantine" + available_for_topredirection = True def init(self): """Init function diff --git a/modoboa/extensions/sievefilters/__init__.py b/modoboa/extensions/sievefilters/__init__.py index c5b88c95f..a6e437c6c 100644 --- a/modoboa/extensions/sievefilters/__init__.py +++ b/modoboa/extensions/sievefilters/__init__.py @@ -11,6 +11,7 @@ class SieveFilters(ModoExtension): version = "1.0" description = ugettext_lazy("Plugin to easily create server-side filters") url = "sfilters" + available_for_topredirection = True def load(self): from .app_settings import ParametersForm, UserSettings diff --git a/modoboa/extensions/webmail/__init__.py b/modoboa/extensions/webmail/__init__.py index 8b336b8b1..d673156f0 100644 --- a/modoboa/extensions/webmail/__init__.py +++ b/modoboa/extensions/webmail/__init__.py @@ -11,6 +11,7 @@ class Webmail(ModoExtension): version = "1.0" description = ugettext_lazy("Simple IMAP webmail") needs_media = True + available_for_topredirection = True def load(self): from .app_settings import ParametersForm, UserSettings From 77e5d754fbf7beb49357a23aa383ce30136234cd Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Mon, 4 Nov 2013 10:35:43 +0100 Subject: [PATCH 10/10] Display help messages on 'hover' event. fixes #492 --- modoboa/static/js/global.js | 5 +++-- modoboa/static/js/twocols_nav.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modoboa/static/js/global.js b/modoboa/static/js/global.js index d58a2a1d4..0e7d05a52 100644 --- a/modoboa/static/js/global.js +++ b/modoboa/static/js/global.js @@ -45,7 +45,8 @@ function modalbox(e, css, defhref, defcb, defclosecb) { $div.modal({show: false}); $div.one('shown', function() { $(".help").popover({ - container: "#modalbox" + container: "#modalbox", + trigger: "hover" }).click(function(e) {e.preventDefault();}); if (modalcb != undefined) { if (typeof modalcb === "function") modalcb(); else eval(modalcb + '()'); @@ -253,5 +254,5 @@ $(document).ready(function() { $(document).on('click', 'a[data-toggle="ajaxmodal"]', modalbox); $(document).on('click', 'a[data-toggle="ajaxmodal-autowidth"]', modalbox_autowidth); $(document).on('click', '.activator', activate_widget); - $(".help").popover().click(function(e) {e.preventDefault();}); + $(".help").popover({trigger: 'hover'}).click(function(e) {e.preventDefault();}); }); \ No newline at end of file diff --git a/modoboa/static/js/twocols_nav.js b/modoboa/static/js/twocols_nav.js index d8d3ed337..ba1d994c4 100644 --- a/modoboa/static/js/twocols_nav.js +++ b/modoboa/static/js/twocols_nav.js @@ -75,7 +75,7 @@ TwocolsNav.prototype = { $('#' + this.options.divid + ' input[type=radio]').click($.proxy(this.radio_clicked, this)); $(".help").popover({ placement: 'bottom', - trigger: 'click' + trigger: 'hover' }).click(function(e) {e.preventDefault();}); $('#' + this.options.divid + ' select').change(); $('#' + this.options.divid + ' input[type=radio]:checked').click();