diff --git a/actions/roundcube b/actions/roundcube new file mode 100755 index 000000000..fa9c4efc0 --- /dev/null +++ b/actions/roundcube @@ -0,0 +1,107 @@ +#!/usr/bin/python3 +# -*- mode: python -*- +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Configuration helper for Roundcube server. +""" + +import argparse +import re +import subprocess + + +APACHE_CONF = '/etc/apache2/conf-available/roundcube.conf' +APACHE_ENABLED_CONF = '/etc/apache2/conf-enabled/roundcube.conf' + + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + subparsers.add_parser('pre-install', + help='Perform Roundcube pre-install configuration') + subparsers.add_parser('setup', + help='Perform Roundcube configuration setup') + subparsers.add_parser('get-enabled', + help='Get whether Roundcube service is enabled') + subparsers.add_parser('enable', help='Enable Roundcube') + subparsers.add_parser('disable', help='Disable Roundcube') + + return parser.parse_args() + + +def subcommand_pre_install(_): + """Preseed debconf values before packages are installed.""" + subprocess.check_output( + ['debconf-set-selections'], + input=b'roundcube-core roundcube/dbconfig-install boolean true') + subprocess.check_output( + ['debconf-set-selections'], + input=b'roundcube-core roundcube/database-type string sqlite3') + + +def subcommand_setup(_): + """Setup Roundcube Apache configuration.""" + with open(APACHE_CONF, 'r') as conffile: + lines = conffile.readlines() + + with open(APACHE_CONF, 'w') as conffile: + for line in lines: + match = re.match(r'#\s*(Alias /roundcube.*)', line) + if match: + conffile.write(match.group(1) + '\n') + else: + conffile.write(line) + + subprocess.call(['service', 'apache2', 'reload']) + + +def subcommand_get_enabled(_): + """Get whether service is enabled.""" + try: + subprocess.check_output(['a2query', '-c', 'roundcube']) + print('yes') + except subprocess.CalledProcessError: + print('no') + + +def subcommand_enable(_): + """Start service.""" + subprocess.call(['a2enconf', 'roundcube']) + subprocess.call(['service', 'apache2', 'reload']) + + +def subcommand_disable(_): + """Stop service.""" + subprocess.call(['a2disconf', 'roundcube']) + subprocess.call(['service', 'apache2', 'reload']) + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method(arguments) + + +if __name__ == '__main__': + main() diff --git a/data/etc/plinth/modules-enabled/roundcube b/data/etc/plinth/modules-enabled/roundcube new file mode 100644 index 000000000..2a953ae33 --- /dev/null +++ b/data/etc/plinth/modules-enabled/roundcube @@ -0,0 +1 @@ +plinth.modules.roundcube diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py new file mode 100644 index 000000000..dd34c8a47 --- /dev/null +++ b/plinth/modules/roundcube/__init__.py @@ -0,0 +1,36 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Plinth module to configure Roundcube. +""" + +from gettext import gettext as _ + +from plinth import actions +from plinth import cfg +from plinth import service as service_module + + +depends = ['plinth.modules.apps'] + + +def init(): + """Intialize the module.""" + menu = cfg.main_menu.get('apps:index') + menu.add_urlname(_('Email Client (Roundcube)'), 'glyphicon-envelope', + 'roundcube:index', 50) diff --git a/plinth/modules/roundcube/forms.py b/plinth/modules/roundcube/forms.py new file mode 100644 index 000000000..a88987558 --- /dev/null +++ b/plinth/modules/roundcube/forms.py @@ -0,0 +1,30 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Forms for configuring Roundcube. +""" + +from django import forms +from gettext import gettext as _ + + +class RoundcubeForm(forms.Form): + """Roundcube configuration form.""" + enabled = forms.BooleanField( + label=_('Enable Roundcube'), + required=False) diff --git a/plinth/modules/roundcube/templates/roundcube.html b/plinth/modules/roundcube/templates/roundcube.html new file mode 100644 index 000000000..7b47dbaab --- /dev/null +++ b/plinth/modules/roundcube/templates/roundcube.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% comment %} +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} + +{% load bootstrap %} + +{% block content %} + +

Email Client (Roundcube)

+ +

Roundcube webmail is a browser-based multilingual IMAP client with + an application-like user interface. It provides full functionality + you expect from an email client, including MIME support, address + book, folder manipulation, message searching and spell checking.

+ +

You can access Roundcube from /roundcube. + Provide the username and password of the email account you wish to + access followed by the domain name of the IMAP server for your email + provider, like imap.example.com. For IMAP over SSL + (recommended), fill the server field + like imaps://imap.example.com.

+ +

For Gmail, username will be your Gmail address, password will be + your Google account password and server will be + imaps://imap.gmail.com. Note that you will also need + to enable "Less secure apps" in your Google account settings + (https://www.google.com/settings/security/lesssecureapps).

+ + +

Configuration

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+ +{% endblock %} diff --git a/plinth/modules/roundcube/urls.py b/plinth/modules/roundcube/urls.py new file mode 100644 index 000000000..1950d1ffa --- /dev/null +++ b/plinth/modules/roundcube/urls.py @@ -0,0 +1,28 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +URLs for the Roundcube module. +""" + +from django.conf.urls import patterns, url + + +urlpatterns = patterns( + 'plinth.modules.roundcube.views', + url(r'^apps/roundcube/$', 'index', name='index'), + ) diff --git a/plinth/modules/roundcube/views.py b/plinth/modules/roundcube/views.py new file mode 100644 index 000000000..eca875e98 --- /dev/null +++ b/plinth/modules/roundcube/views.py @@ -0,0 +1,90 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Plinth module for configuring Roundcube. +""" + +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.template.response import TemplateResponse +from gettext import gettext as _ +import logging + +from .forms import RoundcubeForm +from plinth import actions +from plinth import package + +logger = logging.getLogger(__name__) + + +def before_install(): + """Preseed debconf values before the packages are installed.""" + actions.superuser_run('roundcube', ['pre-install']) + + +def on_install(): + """Setup Roundcube Apache configuration.""" + actions.superuser_run('roundcube', ['setup']) + + +@login_required +@package.required(['sqlite3', 'roundcube', 'roundcube-sqlite3'], + before_install=before_install, on_install=on_install) +def index(request): + """Serve configuration page.""" + status = get_status() + + form = None + + if request.method == 'POST': + form = RoundcubeForm(request.POST, prefix='roundcube') + # pylint: disable=E1101 + if form.is_valid(): + _apply_changes(request, status, form.cleaned_data) + status = get_status() + form = RoundcubeForm(initial=status, prefix='roundcube') + else: + form = RoundcubeForm(initial=status, prefix='roundcube') + + return TemplateResponse(request, 'roundcube.html', + {'title': _('Email Client (Roundcube)'), + 'status': status, + 'form': form}) + + +def get_status(): + """Get the current status.""" + output = actions.run('roundcube', ['get-enabled']) + enabled = (output.strip() == 'yes') + + return {'enabled': enabled} + + +def _apply_changes(request, old_status, new_status): + """Apply the changes.""" + modified = False + + if old_status['enabled'] != new_status['enabled']: + sub_command = 'enable' if new_status['enabled'] else 'disable' + actions.superuser_run('roundcube', [sub_command]) + modified = True + + if modified: + messages.success(request, _('Configuration updated')) + else: + messages.info(request, _('Setting unchanged'))