Projects
Kolab:3.4
pykolab
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 148
View file
pykolab.spec
Changed
@@ -28,19 +28,13 @@ Summary: Kolab Groupware Solution Name: pykolab -Version: 0.7.8 -Release: 3%{?dist} +Version: 0.7.9 +Release: 1%{?dist} License: GPLv3+ Group: Applications/System URL: http://kolab.org/ Source0: http://files.kolab.org/releases/%{name}-%{version}.tar.gz -Patch4: remove-plugin-threading_as_default.patch -Patch5: deliver-to-shared-folders-with-spaces.patch -Patch6: fix_kolabsaslauthd_centos7.patch -Patch7: fix_run_kolabd_centos7.patch -Patch8: fix_clamd_sock_centos7.patch -Patch9: fix_wallace_pid_location_centos7.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildArch: noarch @@ -206,15 +200,8 @@ %prep %setup -q -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 -%patch8 -p1 -%patch9 -p1 - %build -autoreconf -v +autoreconf -v || automake --add-missing && autoreconf -v %configure %install @@ -535,8 +522,10 @@ %attr(0700,%{kolab_user},%{kolab_group}) %dir %{_var}/spool/pykolab/wallace %changelog -* Mon Feb 23 2015 Timotheus Pokorra (TBits.net) <tp@tbits.net> -- fix location of wallace pid file on CentOS7, #4673 +* Mon Feb 23 2015 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.7.9-1 +- Release of version 0.7.9, see; + + https://issues.kolab.org/buglist.cgi?target_milestone=0.7.9&product=pykolab * Fri Feb 20 2015 Timotheus Pokorra (TBits.net) <tp@tbits.net> - more fixes for CentOS7, fix path for /run/kolabd, fixing #2626
View file
deliver-to-shared-folders-with-spaces.patch
Deleted
@@ -1,25 +0,0 @@ -From 1ae974030e1e71565872f7265ba33f1cd82986bd Mon Sep 17 00:00:00 2001 -From: Daniel Hoffend <dh@dotlan.net> -Date: Wed, 18 Feb 2015 00:55:40 +0100 -Subject: [PATCH] deliver to shared folders with spaces #4613 - ---- - pykolab/setup/setup_mta.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/pykolab/setup/setup_mta.py b/pykolab/setup/setup_mta.py -index 88c6f6c..0cd9c33 100644 ---- a/pykolab/setup/setup_mta.py -+++ b/pykolab/setup/setup_mta.py -@@ -226,7 +226,7 @@ bind_pw = %(service_bind_pw)s - - query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabsharedfolder)(kolabFolderType=mail)) - result_attribute = kolabtargetfolder --result_format = shared+%%s -+result_format = "shared+%%s" - """ % { - "base_dn": conf.get('ldap', 'base_dn'), - "server_host": server_host, --- -1.9.1 -
View file
fix_clamd_sock_centos7.patch
Deleted
@@ -1,54 +0,0 @@ -From e43e509c773c7f69ab4c479c708d20aae0cf063d Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Thu, 19 Feb 2015 13:38:54 +0100 -Subject: [PATCH 2/2] CentOS7: make sure we are using the correct path for the - clamd.sock (#3565) reading from - /etc/clamd.d/amavisd.conf, otherwise defaulting to - /var/spool/amavisd/clamd.sock which was previously used - ---- - pykolab/setup/setup_mta.py | 7 +++++++ - share/templates/amavisd.conf.tpl | 2 +- - 2 files changed, 8 insertions(+), 1 deletion(-) - -diff --git a/pykolab/setup/setup_mta.py b/pykolab/setup/setup_mta.py -index 0cd9c33..56109fe 100644 ---- a/pykolab/setup/setup_mta.py -+++ b/pykolab/setup/setup_mta.py -@@ -363,6 +363,7 @@ result_format = "shared+%%s" - 'primary_domain': conf.get('kolab', 'primary_domain'), - 'ldap_filter': "(|(mail=%m)(alias=%m))", - 'ldap_base_dn': conf.get('ldap', 'base_dn'), -+ 'clamdsock': '/var/spool/amavisd/clamd.sock', - } - - template_file = None -@@ -381,6 +382,12 @@ result_format = "shared+%%s" - template_definition = fp.read() - fp.close() - -+ if os.path.isfile('/etc/clamd.d/amavisd.conf'): -+ amavisdconf_content = file('/etc/clamd.d/amavisd.conf') -+ for line in amavisdconf_content: -+ if line.startswith('LocalSocket'): -+ amavisd_settings['clamdsock'] = line[len('LocalSocket '):].strip() -+ - t = Template(template_definition, searchList=[amavisd_settings]) - - fp = None -diff --git a/share/templates/amavisd.conf.tpl b/share/templates/amavisd.conf.tpl -index 12fb4ed..1fa43fb 100644 ---- a/share/templates/amavisd.conf.tpl -+++ b/share/templates/amavisd.conf.tpl -@@ -373,7 +373,7 @@ use strict; - - # \#\## http://www.clamav.net/ - ['ClamAV-clamd', -- \&ask_daemon, ["CONTSCAN {}\n", "/var/spool/amavisd/clamd.sock"], -+ \&ask_daemon, ["CONTSCAN {}\n", "$clamdsock"], - qr/\bOK\$/m, qr/\bFOUND\$/m, - qr/^.*?: (?!Infected Archive)(.*) FOUND\$/m ], - # # NOTE: run clamd under the same user as amavisd, or run it under its own --- -1.7.9.5 -
View file
fix_kolabsaslauthd_centos7.patch
Deleted
@@ -1,26 +0,0 @@ -From 8ffb806fd0a0ff53dbb38daa00aee542785b323b Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Thu, 19 Feb 2015 10:54:24 +0100 -Subject: [PATCH] CentOS7 systemd: fix location of kolab-saslauthd socket - (#4628) from the default of /var/run/saslauthd/mux to /run/saslauthd/mux - ---- - saslauthd/kolab-saslauthd.systemd | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/saslauthd/kolab-saslauthd.systemd b/saslauthd/kolab-saslauthd.systemd -index a39073c..f54b9d0 100644 ---- a/saslauthd/kolab-saslauthd.systemd -+++ b/saslauthd/kolab-saslauthd.systemd -@@ -6,7 +6,7 @@ After=syslog.target network.target - Type=forking - PIDFile=/var/run/kolab-saslauthd/kolab-saslauthd.pid - EnvironmentFile=/etc/sysconfig/kolab-saslauthd --ExecStart=/usr/sbin/kolab-saslauthd $FLAGS -+ExecStart=/usr/sbin/kolab-saslauthd $FLAGS --socket /run/saslauthd/mux - ExecReload=/bin/kill -HUP $MAINPID - ExecStop=/bin/kill -TERM $MAINPID - --- -1.8.3.1 -
View file
fix_run_kolabd_centos7.patch
Deleted
@@ -1,23 +0,0 @@ -From ae9894cee0f8b85978d6b96d57d0a006d2c975ae Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Fri, 20 Feb 2015 07:15:23 +0100 -Subject: [PATCH 1/2] CentOS7 systemd: need to adjust the - kolabd.tmpfiles.d.conf as well (#2626) that was - pointing still to /var/run/kolabd instead of - /run/kolabd as in the spec file - ---- - kolabd/kolabd.tmpfiles.d.conf | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/kolabd/kolabd.tmpfiles.d.conf b/kolabd/kolabd.tmpfiles.d.conf -index ce98b6e..abea15c 100644 ---- a/kolabd/kolabd.tmpfiles.d.conf -+++ b/kolabd/kolabd.tmpfiles.d.conf -@@ -1,2 +1,2 @@ --d /var/run/kolabd 750 kolab kolab -+d /run/kolabd 750 kolab kolab - --- -1.7.9.5 -
View file
fix_wallace_pid_location_centos7.patch
Deleted
@@ -1,40 +0,0 @@ -From 2560d642b84828e6feba4d934b3b7f8e589d141a Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Mon, 23 Feb 2015 07:31:21 +0100 -Subject: [PATCH] CentOS7 systemd: use /run/wallaced as location for the pid file (#4673) - instead of a mix of /var/run/wallaced and /run/wallaced - ---- - wallace/wallace.systemd | 4 ++-- - wallace/wallace.tmpfiles.d.conf | 2 +- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/wallace/wallace.systemd b/wallace/wallace.systemd -index 38a6604..c033269 100644 ---- a/wallace/wallace.systemd -+++ b/wallace/wallace.systemd -@@ -4,11 +4,11 @@ After=syslog.target network.target - - [Service] - Type=forking --PIDFile=/var/run/wallaced/wallaced.pid -+PIDFile=/run/wallaced/wallaced.pid - User=kolab - Group=kolab - EnvironmentFile=/etc/sysconfig/wallace --ExecStart=/usr/sbin/wallaced $FLAGS -+ExecStart=/usr/sbin/wallaced $FLAGS --pid-file /run/wallaced/wallaced.pid - ExecReload=/bin/kill -HUP $MAINPID - ExecStop=/bin/kill -TERM $MAINPID - -diff --git a/wallace/wallace.tmpfiles.d.conf b/wallace/wallace.tmpfiles.d.conf -index 7dd659a..b072311 100644 ---- a/wallace/wallace.tmpfiles.d.conf -+++ b/wallace/wallace.tmpfiles.d.conf -@@ -1,2 +1,2 @@ --d /var/run/wallaced 750 kolab kolab -+d /run/wallaced 750 kolab kolab - --- -1.7.1 -
View file
remove-plugin-threading_as_default.patch
Deleted
@@ -1,24 +0,0 @@ -From dc75af98a0caf858d5e35100f9b6a51bc4b21309 Mon Sep 17 00:00:00 2001 -From: Daniel Hoffend <dh@dotlan.net> -Date: Sun, 15 Feb 2015 01:07:59 +0100 -Subject: [PATCH] plugin threading_as_default no longer exists #4570 - ---- - share/templates/roundcubemail/config.inc.php.tpl | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/share/templates/roundcubemail/config.inc.php.tpl b/share/templates/roundcubemail/config.inc.php.tpl -index 77cadab..7206a09 100644 ---- a/share/templates/roundcubemail/config.inc.php.tpl -+++ b/share/templates/roundcubemail/config.inc.php.tpl -@@ -68,7 +68,6 @@ - 'password', - 'redundant_attachments', - 'tasklist', -- 'threading_as_default', - // contextmenu must be after kolab_addressbook (#444) - 'contextmenu', - ); --- -1.9.1 -
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +pykolab (0.7.9-0~kolab1) unstable; urgency=low + + * https://issues.kolab.org/buglist.cgi?target_milestone=0.7.9&product=pykolab + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Mon, 23 Feb 2015 01:49:00 +0100 + pykolab (0.7.8-0~kolab3) unstable; urgency=low * deliver to shared folders with spaces #4613
View file
pykolab-0.7.8.tar.gz/configure.ac -> pykolab-0.7.9.tar.gz/configure.ac
Changed
@@ -1,4 +1,4 @@ -AC_INIT([pykolab], 0.7.8) +AC_INIT([pykolab], 0.7.9) AC_SUBST([RELEASE], 1) AC_CONFIG_SRCDIR(pykolab/constants.py.in)
View file
pykolab-0.7.8.tar.gz/kolabd/kolabd.tmpfiles.d.conf -> pykolab-0.7.9.tar.gz/kolabd/kolabd.tmpfiles.d.conf
Changed
@@ -1,2 +1,2 @@ -d /var/run/kolabd 750 kolab kolab +d /run/kolabd 750 kolab kolab
View file
pykolab-0.7.8.tar.gz/po/de.po -> pykolab-0.7.9.tar.gz/po/de.po
Changed
@@ -5,7 +5,8 @@ # Translators: # Christoph Wickert <christoph.wickert@gmail.com>, 2015 # Christoph Wickert <christoph.wickert@gmail.com>, 2011 -# Ettore Atalan <atalanttore@googlemail.com>, 2014 +# Dirk Marschner <info@marschner.cx>, 2015 +# Ettore Atalan <atalanttore@googlemail.com>, 2014-2015 # Torsten Grote <grote@kolabsys.com>, 2012 # Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>, 2015 # balin <johannes_graumann@web.de>, 2012 @@ -18,8 +19,8 @@ "Project-Id-Version: Kolab Groupware Solution\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-14 01:36+0100\n" -"PO-Revision-Date: 2015-01-23 15:10+0000\n" -"Last-Translator: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>\n" +"PO-Revision-Date: 2015-02-01 15:31+0000\n" +"Last-Translator: Dirk Marschner <info@marschner.cx>\n" "Language-Team: German (http://www.transifex.com/projects/p/kolab/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -44,12 +45,12 @@ #: ../bin/kolab_smtp_access_policy.py:528 #, python-format msgid "Could not find envelope sender user %s (511)" -msgstr "" +msgstr "Konnte Absender-Umschlag für Benutzer %s nicht finden (511)" #: ../bin/kolab_smtp_access_policy.py:571 #, python-format msgid "Obtained authenticated user details for %r: %r" -msgstr "" +msgstr "Erhaltene Details des authentifizieren Benutzers %r: %r" #: ../bin/kolab_smtp_access_policy.py:628 #, python-format @@ -78,18 +79,18 @@ msgid "" "Verifying authenticated sender '%(sender)s' with sasl_username " "'%(sasl_username)s' for recipient '%(recipient)s'" -msgstr "" +msgstr "Überprüfe authentisierten Absender '%(sender)s' mit SASL-Benutzernamen '%(sasl_username)s' für Empfänger '%(recipient)s'" #: ../bin/kolab_smtp_access_policy.py:752 #, python-format msgid "" "Verifying unauthenticated sender '%(sender)s' for recipient '%(recipient)s'" -msgstr "" +msgstr "Überprüfe unauthentisierten Absender '%(sender)s' für Empfänger '%(recipient)s'" #: ../bin/kolab_smtp_access_policy.py:768 #, python-format msgid "Reproducing verify_recipient(%s, %s) from cache" -msgstr "" +msgstr "Reproduziere verify_recipient(%s, %s) aus Cache" #: ../bin/kolab_smtp_access_policy.py:805 #, python-format @@ -106,20 +107,20 @@ msgid "" "Checking the recipient for domain %s that is not ours. This is probably a " "configuration error." -msgstr "" +msgstr "Überprüfe Empfänger für fremde Domäne %s. Dies ist wahrscheinlich ein Konfigurationsfehler." #: ../bin/kolab_smtp_access_policy.py:838 msgid "" "This recipient address is related to multiple object entries and the SMTP " "Access Policy can therefore not restrict message flow" -msgstr "" +msgstr "Diese Empfänger-Adresse bezieht sich auf mehrere Objekt-Einträge. Die SMTP Access Policy kann deshalb den Nachrichtenfluss nicht einschränken." #: ../bin/kolab_smtp_access_policy.py:855 #, python-format msgid "" "Recipient address %r not found. Allowing since the MTA was configured to " "accept the recipient." -msgstr "" +msgstr "Emfänger-Adresse %r nicht gefunden. Erlauben, da MTA konfiguriert wurde, diesen Empfänger zu akzeptieren." #: ../bin/kolab_smtp_access_policy.py:891 msgid "Invalid recipient" @@ -133,24 +134,24 @@ #: ../bin/kolab_smtp_access_policy.py:1051 #, python-format msgid "Sender %s is not allowed to send to recipient %s" -msgstr "" +msgstr "Absender %s darf nicht an Empfänger %s senden" #: ../bin/kolab_smtp_access_policy.py:1039 #, python-format msgid "Reproducing verify_sender(%r) from cache" -msgstr "" +msgstr "Reproduziere verify_sender(%r) aus Cache" #: ../bin/kolab_smtp_access_policy.py:1056 msgid "Unverifiable sender." -msgstr "" +msgstr "Nicht überprüfbarer Absender." #: ../bin/kolab_smtp_access_policy.py:1061 msgid "Sender is not using an alias" -msgstr "" +msgstr "Absender nutzt keinen Alias" #: ../bin/kolab_smtp_access_policy.py:1069 msgid "Sender uses unauthorized envelope sender address" -msgstr "" +msgstr "Absender nutzt unerlaubte Absender-Umschlag Adresse" #: ../bin/kolab_smtp_access_policy.py:1086 msgid "Could not verify sender" @@ -159,7 +160,7 @@ #: ../bin/kolab_smtp_access_policy.py:1093 msgid "" "Verifying whether sender is allowed to send to recipient using sender policy" -msgstr "" +msgstr "Überprüfe ob Absender an Empfänger gemäß Absender-Richtlinie senden darf" #: ../bin/kolab_smtp_access_policy.py:1106 #, python-format @@ -177,7 +178,7 @@ #: ../bin/kolab_smtp_access_policy.py:1135 #, python-format msgid "Sender %s not allowed to send to recipient %s" -msgstr "" +msgstr "Absender %s darf nicht an Empfänger %s senden" #: ../bin/kolab_smtp_access_policy.py:1156 msgid "Cleaning up the cache" @@ -187,42 +188,42 @@ msgid "" "The 'uri' setting in the kolab_smtp_access_policy section is soon going to " "be deprecated in favor of 'cache_uri'" -msgstr "" +msgstr "Die 'uri' Einstellung im Abschnitt kolab_smtp_access_policy wird bald überholt sein. Nutzen Sie 'cache_uri' stattdessen " #: ../bin/kolab_smtp_access_policy.py:1195 #, python-format msgid "Operational Error in caching: %s" -msgstr "" +msgstr "Betriebsbedingter Fehler beim Cachen: %s" #: ../bin/kolab_smtp_access_policy.py:1247 #, python-format msgid "Caching the policy result with timestamp %d" -msgstr "" +msgstr "Cachen des Richtlinienergebnisses mit Zeitstempel %d" #: ../bin/kolab_smtp_access_policy.py:1321 #, python-format msgid "Returning action DEFER_IF_PERMIT: %s" -msgstr "" +msgstr "Antwort Aktion DEFER_IF_PERMIT: %s" #: ../bin/kolab_smtp_access_policy.py:1326 #, python-format msgid "Returning action DUNNO: %s" -msgstr "" +msgstr "Antwort Aktion DUNNO: %s" #: ../bin/kolab_smtp_access_policy.py:1331 #, python-format msgid "Returning action HOLD: %s" -msgstr "" +msgstr "Antwort Aktion HOLD: %s" #: ../bin/kolab_smtp_access_policy.py:1336 #, python-format msgid "Returning action PERMIT: %s" -msgstr "" +msgstr "Antwort Aktion PERMIT: %s" #: ../bin/kolab_smtp_access_policy.py:1461 #, python-format msgid "Returning action REJECT: %s" -msgstr "" +msgstr "Antwort Aktion REJECT: %s" #: ../bin/kolab_smtp_access_policy.py:1507 msgid "Starting to loop for new request" @@ -230,7 +231,7 @@ #: ../bin/kolab_smtp_access_policy.py:1514 msgid "Timeout for policy request reading exceeded" -msgstr "" +msgstr "Zeitüberschreitung beim Lesen der Richtlinienanfrage" #: ../bin/kolab_smtp_access_policy.py:1520 msgid "End of current request" @@ -239,11 +240,11 @@ #: ../bin/kolab_smtp_access_policy.py:1524 #, python-format msgid "Getting line: %s" -msgstr "" +msgstr "Lese Zeile: %s" #: ../bin/kolab_smtp_access_policy.py:1528 msgid "Returning request" -msgstr ""
View file
pykolab-0.7.8.tar.gz/po/de_DE.po -> pykolab-0.7.9.tar.gz/po/de_DE.po
Changed
@@ -3,21 +3,29 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Christoph Wickert <christoph.wickert@gmail.com>, 2012 +# Christoph Wickert <christoph.wickert@gmail.com>, 2015 +# Christoph Wickert <christoph.wickert@gmail.com>, 2011 +# Dirk Marschner <info@marschner.cx>, 2015 +# Ettore Atalan <atalanttore@googlemail.com>, 2014-2015 # Torsten Grote <grote@kolabsys.com>, 2012 +# Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>, 2015 +# balin <johannes_graumann@web.de>, 2012 +# Jo <jo@caj-augsburg.de>, 2012 +# bitnukl, 2014 # Thomas Brüderli <roundcube@gmail.com>, 2014 +# Till Savekoul <till@koul.de>, 2012 msgid "" msgstr "" "Project-Id-Version: Kolab Groupware Solution\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-14 01:36+0100\n" -"PO-Revision-Date: 2015-01-21 16:28+0000\n" -"Last-Translator: Thomas Brüderli <roundcube@gmail.com>\n" -"Language-Team: German (Germany) (http://www.transifex.com/projects/p/kolab/language/de_DE/)\n" +"PO-Revision-Date: 2015-02-01 15:31+0000\n" +"Last-Translator: Dirk Marschner <info@marschner.cx>\n" +"Language-Team: German (http://www.transifex.com/projects/p/kolab/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: de_DE\n" +"Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../bin/kolab_smtp_access_policy.py:210 @@ -37,12 +45,12 @@ #: ../bin/kolab_smtp_access_policy.py:528 #, python-format msgid "Could not find envelope sender user %s (511)" -msgstr "" +msgstr "Konnte Absender-Umschlag für Benutzer %s nicht finden (511)" #: ../bin/kolab_smtp_access_policy.py:571 #, python-format msgid "Obtained authenticated user details for %r: %r" -msgstr "" +msgstr "Erhaltene Details des authentifizieren Benutzers %r: %r" #: ../bin/kolab_smtp_access_policy.py:628 #, python-format @@ -58,7 +66,7 @@ #, python-format msgid "" "User %s attempted to use envelope sender address %s without authorization" -msgstr "" +msgstr "Benutzer %s versuchte die Absendeadresse %s ohne Berechtigung zu verwenden" #: ../bin/kolab_smtp_access_policy.py:714 #: ../bin/kolab_smtp_access_policy.py:725 @@ -71,23 +79,23 @@ msgid "" "Verifying authenticated sender '%(sender)s' with sasl_username " "'%(sasl_username)s' for recipient '%(recipient)s'" -msgstr "" +msgstr "Überprüfe authentisierten Absender '%(sender)s' mit SASL-Benutzernamen '%(sasl_username)s' für Empfänger '%(recipient)s'" #: ../bin/kolab_smtp_access_policy.py:752 #, python-format msgid "" "Verifying unauthenticated sender '%(sender)s' for recipient '%(recipient)s'" -msgstr "" +msgstr "Überprüfe unauthentisierten Absender '%(sender)s' für Empfänger '%(recipient)s'" #: ../bin/kolab_smtp_access_policy.py:768 #, python-format msgid "Reproducing verify_recipient(%s, %s) from cache" -msgstr "" +msgstr "Reproduziere verify_recipient(%s, %s) aus Cache" #: ../bin/kolab_smtp_access_policy.py:805 #, python-format msgid "Using authentication domain %s instead of %s" -msgstr "Benutze Authentisierungsdomain %s anstelle von %s" +msgstr "Authentisierungsdomain %s anstelle von %s wird verwendet" #: ../bin/kolab_smtp_access_policy.py:815 #, python-format @@ -99,20 +107,20 @@ msgid "" "Checking the recipient for domain %s that is not ours. This is probably a " "configuration error." -msgstr "" +msgstr "Überprüfe Empfänger für fremde Domäne %s. Dies ist wahrscheinlich ein Konfigurationsfehler." #: ../bin/kolab_smtp_access_policy.py:838 msgid "" "This recipient address is related to multiple object entries and the SMTP " "Access Policy can therefore not restrict message flow" -msgstr "" +msgstr "Diese Empfänger-Adresse bezieht sich auf mehrere Objekt-Einträge. Die SMTP Access Policy kann deshalb den Nachrichtenfluss nicht einschränken." #: ../bin/kolab_smtp_access_policy.py:855 #, python-format msgid "" "Recipient address %r not found. Allowing since the MTA was configured to " "accept the recipient." -msgstr "" +msgstr "Emfänger-Adresse %r nicht gefunden. Erlauben, da MTA konfiguriert wurde, diesen Empfänger zu akzeptieren." #: ../bin/kolab_smtp_access_policy.py:891 msgid "Invalid recipient" @@ -120,30 +128,30 @@ #: ../bin/kolab_smtp_access_policy.py:902 msgid "Could not find this user, accepting" -msgstr "" +msgstr "Konnte keine Einschränkung für diesen Benutzer finden, akzeptiere Nachricht" #: ../bin/kolab_smtp_access_policy.py:975 #: ../bin/kolab_smtp_access_policy.py:1051 #, python-format msgid "Sender %s is not allowed to send to recipient %s" -msgstr "" +msgstr "Absender %s darf nicht an Empfänger %s senden" #: ../bin/kolab_smtp_access_policy.py:1039 #, python-format msgid "Reproducing verify_sender(%r) from cache" -msgstr "" +msgstr "Reproduziere verify_sender(%r) aus Cache" #: ../bin/kolab_smtp_access_policy.py:1056 msgid "Unverifiable sender." -msgstr "" +msgstr "Nicht überprüfbarer Absender." #: ../bin/kolab_smtp_access_policy.py:1061 msgid "Sender is not using an alias" -msgstr "" +msgstr "Absender nutzt keinen Alias" #: ../bin/kolab_smtp_access_policy.py:1069 msgid "Sender uses unauthorized envelope sender address" -msgstr "" +msgstr "Absender nutzt unerlaubte Absender-Umschlag Adresse" #: ../bin/kolab_smtp_access_policy.py:1086 msgid "Could not verify sender" @@ -152,7 +160,7 @@ #: ../bin/kolab_smtp_access_policy.py:1093 msgid "" "Verifying whether sender is allowed to send to recipient using sender policy" -msgstr "" +msgstr "Überprüfe ob Absender an Empfänger gemäß Absender-Richtlinie senden darf" #: ../bin/kolab_smtp_access_policy.py:1106 #, python-format @@ -170,7 +178,7 @@ #: ../bin/kolab_smtp_access_policy.py:1135 #, python-format msgid "Sender %s not allowed to send to recipient %s" -msgstr "" +msgstr "Absender %s darf nicht an Empfänger %s senden" #: ../bin/kolab_smtp_access_policy.py:1156 msgid "Cleaning up the cache" @@ -180,42 +188,42 @@ msgid "" "The 'uri' setting in the kolab_smtp_access_policy section is soon going to " "be deprecated in favor of 'cache_uri'" -msgstr "" +msgstr "Die 'uri' Einstellung im Abschnitt kolab_smtp_access_policy wird bald überholt sein. Nutzen Sie 'cache_uri' stattdessen " #: ../bin/kolab_smtp_access_policy.py:1195 #, python-format msgid "Operational Error in caching: %s" -msgstr "" +msgstr "Betriebsbedingter Fehler beim Cachen: %s" #: ../bin/kolab_smtp_access_policy.py:1247 #, python-format msgid "Caching the policy result with timestamp %d" -msgstr "" +msgstr "Cachen des Richtlinienergebnisses mit Zeitstempel %d" #: ../bin/kolab_smtp_access_policy.py:1321 #, python-format msgid "Returning action DEFER_IF_PERMIT: %s" -msgstr "" +msgstr "Antwort Aktion DEFER_IF_PERMIT: %s" #: ../bin/kolab_smtp_access_policy.py:1326 #, python-format msgid "Returning action DUNNO: %s" -msgstr "" +msgstr "Antwort Aktion DUNNO: %s" #: ../bin/kolab_smtp_access_policy.py:1331 #, python-format msgid "Returning action HOLD: %s"
View file
pykolab-0.7.8.tar.gz/pykolab/itip/__init__.py -> pykolab-0.7.9.tar.gz/pykolab/itip/__init__.py
Changed
@@ -1,6 +1,7 @@ import icalendar import pykolab import traceback +import kolabformat from pykolab.xml import to_dt from pykolab.xml import event_from_ical @@ -86,6 +87,7 @@ itip['uid'] = str(c['uid']) itip['method'] = str(cal['method']).upper() itip['sequence'] = int(c['sequence']) if c.has_key('sequence') else 0 + itip['recurrence-id'] = c['recurrence-id'].dt if c.has_key('recurrence-id') and hasattr(c['recurrence-id'], 'dt') else None if c.has_key('dtstart'): itip['start'] = c['dtstart'].dt @@ -151,30 +153,52 @@ return conflict # don't consider conflict if event has TRANSP:TRANSPARENT - if kolab_event.get_transparency(): + if _is_transparent(kolab_event): return conflict _es = to_dt(kolab_event.get_start()) _ee = to_dt(kolab_event.get_ical_dtend()) # use iCal style end date: next day for all-day events + _ev = kolab_event # naive loops to check for collisions in (recurring) events # TODO: compare recurrence rules directly (e.g. matching time slot or weekday or monthday) while not conflict and _es is not None: _is = to_dt(itip_event['start']) _ie = to_dt(itip_event['end']) + _iv = itip_event['xml'] while not conflict and _is is not None: # log.debug("* Comparing event dates at %s/%s with %s/%s" % (_es, _ee, _is, _ie), level=9) - conflict = check_date_conflict(_es, _ee, _is, _ie) - _is = to_dt(itip_event['xml'].get_next_occurence(_is)) if kolab_event.is_recurring() else None + conflict = not _is_transparent(_ev) and not _is_transparent(_iv) and check_date_conflict(_es, _ee, _is, _ie) + _is = to_dt(itip_event['xml'].get_next_occurence(_is)) if itip_event['xml'].is_recurring() else None _ie = to_dt(itip_event['xml'].get_occurence_end_date(_is)) + # get full occurrence to compare the dates from a possible exception + if _is is not None and len(itip_event['xml'].get_exceptions()): + _ix = itip_event['xml'].get_instance(_is) + if _ix is not None: + _is = to_dt(_ix.get_start()) + _ie = to_dt(_ix.get_end()) + _iv = _ix + _es = to_dt(kolab_event.get_next_occurence(_es)) if kolab_event.is_recurring() else None _ee = to_dt(kolab_event.get_occurence_end_date(_es)) + # get full instance to compare the dates from a possible exception + if _es is not None and len(kolab_event.get_exceptions()): + _ex = kolab_event.get_instance(_es) + if _ex is not None: + _es = to_dt(_ex.get_start()) + _ee = to_dt(_ex.get_end()) + _ev = _ex + return conflict +def _is_transparent(event): + return event.get_transparency() or event.get_status() == kolabformat.StatusCancelled + + def check_date_conflict(_es, _ee, _is, _ie): """ Check the given event start/end dates for conflicts
View file
pykolab-0.7.8.tar.gz/pykolab/setup/setup_mta.py -> pykolab-0.7.9.tar.gz/pykolab/setup/setup_mta.py
Changed
@@ -226,7 +226,7 @@ query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabsharedfolder)(kolabFolderType=mail)) result_attribute = kolabtargetfolder -result_format = shared+%%s +result_format = "shared+%%s" """ % { "base_dn": conf.get('ldap', 'base_dn'), "server_host": server_host, @@ -363,6 +363,7 @@ 'primary_domain': conf.get('kolab', 'primary_domain'), 'ldap_filter': "(|(mail=%m)(alias=%m))", 'ldap_base_dn': conf.get('ldap', 'base_dn'), + 'clamdsock': '/var/spool/amavisd/clamd.sock', } template_file = None @@ -381,6 +382,12 @@ template_definition = fp.read() fp.close() + if os.path.isfile('/etc/clamd.d/amavisd.conf'): + amavisdconf_content = file('/etc/clamd.d/amavisd.conf') + for line in amavisdconf_content: + if line.startswith('LocalSocket'): + amavisd_settings['clamdsock'] = line[len('LocalSocket '):].strip() + t = Template(template_definition, searchList=[amavisd_settings]) fp = None
View file
pykolab-0.7.8.tar.gz/pykolab/xml/event.py -> pykolab-0.7.9.tar.gz/pykolab/xml/event.py
Changed
@@ -58,13 +58,14 @@ class Event(object): type = 'event' + thisandfuture = False status_map = { None: kolabformat.StatusUndefined, "TENTATIVE": kolabformat.StatusTentative, "CONFIRMED": kolabformat.StatusConfirmed, "CANCELLED": kolabformat.StatusCancelled, - "COMPLETD": kolabformat.StatusCompleted, + "COMPLETED": kolabformat.StatusCompleted, "IN-PROCESS": kolabformat.StatusInProcess, "NEEDS-ACTION": kolabformat.StatusNeedsAction, } @@ -120,6 +121,7 @@ def __init__(self, from_ical="", from_string=""): self._attendees = [] self._categories = [] + self._exceptions = [] self._attachment_parts = [] if isinstance(from_ical, str) and from_ical == "": @@ -128,6 +130,7 @@ else: self.event = kolabformat.readEvent(from_string, False) self._load_attendees() + self._load_exceptions() else: self.from_ical(from_ical, from_string) @@ -139,10 +142,22 @@ att.copy_from(a) self._attendees.append(att) - def add_attendee(self, email, name=None, rsvp=False, role=None, participant_status=None, cutype="INDIVIDUAL", params=None): - attendee = Attendee(email, name, rsvp, role, participant_status, cutype, params) - self._attendees.append(attendee) - self.event.setAttendees(self._attendees) + def _load_exceptions(self): + for ex in self.event.exceptions(): + exception = Event() + exception.uid = ex.uid() + exception.event = ex + exception._load_attendees() + self._exceptions.append(exception) + + def add_attendee(self, email_or_attendee, name=None, rsvp=False, role=None, participant_status=None, cutype="INDIVIDUAL", params=None): + if isinstance(email_or_attendee, Attendee): + attendee = email_or_attendee + else: + attendee = Attendee(email_or_attendee, name, rsvp, role, participant_status, cutype, params) + + # apply update to self and all exceptions + self.update_attendees([attendee], True) def add_category(self, category): self._categories.append(ustr(category)) @@ -161,10 +176,35 @@ valid_datetime = True if not valid_datetime: - raise InvalidEventDateError, _("Event start needs datetime.date or datetime.datetime instance") + raise InvalidEventDateError, _("Exdate needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)) self.event.addExceptionDate(xmlutils.to_cdatetime(_datetime, True)) + def add_exception(self, exception): + # sanity checks + if not self.is_recurring(): + raise EventIntegrityError, "Cannot add exceptions to a non-recurring event" + + recurrence_id = exception.get_recurrence_id() + if recurrence_id is None: + raise EventIntegrityError, "Recurrence exceptions require a Recurrence-ID property" + + # check if an exception with the given recurrence-id already exists + append = True + vexceptions = self.event.exceptions() + for i, ex in enumerate(self._exceptions): + if ex.get_recurrence_id() == recurrence_id and ex.thisandfuture == exception.thisandfuture: + # update the existing exception + vexceptions[i] = exception.event + self._exceptions[i] = exception + append = False + + if append: + vexceptions.append(exception.event) + self._exceptions.append(exception) + + self.event.setExceptions(vexceptions) + def as_string_itip(self, method="REQUEST"): cal = icalendar.Calendar() cal.add( @@ -184,6 +224,19 @@ # TODO: Add timezone information using icalendar.?() # Not sure if there is a class for it. + cal.add_component(self.to_ical()) + + # add recurrence exceptions + if len(self._exceptions) > 0 and not method == 'REPLY': + for exception in self._exceptions: + cal.add_component(exception.to_ical()) + + if hasattr(cal, 'to_ical'): + return cal.to_ical() + elif hasattr(cal, 'as_string'): + return cal.as_string() + + def to_ical(self): event = icalendar.Event() # Required @@ -191,8 +244,9 @@ # NOTE: Make sure to list(set()) or duplicates may arise for attr in list(set(event.singletons)): - ical_getter = 'get_ical_%s' % (attr.lower()) - default_getter = 'get_%s' % (attr.lower()) + _attr = attr.lower().replace('-', '') + ical_getter = 'get_ical_%s' % (_attr) + default_getter = 'get_%s' % (_attr) retval = None if hasattr(self, ical_getter): retval = getattr(self, ical_getter)() @@ -205,8 +259,9 @@ # NOTE: Make sure to list(set()) or duplicates may arise for attr in list(set(event.multiple)): - ical_getter = 'get_ical_%s' % (attr.lower()) - default_getter = 'get_%s' % (attr.lower()) + _attr = attr.lower().replace('-', '') + ical_getter = 'get_ical_%s' % (_attr) + default_getter = 'get_%s' % (_attr) retval = None if hasattr(self, ical_getter): retval = getattr(self, ical_getter)() @@ -221,12 +276,7 @@ for cs in self.event.customProperties(): event.add(cs.identifier, cs.value) - cal.add_component(event) - - if hasattr(cal, 'to_ical'): - return cal.to_ical() - elif hasattr(cal, 'as_string'): - return cal.as_string() + return event def delegate(self, delegators, delegatees, names=None): if not isinstance(delegators, list): @@ -261,7 +311,7 @@ self.event.setAttendees(self._attendees) def from_ical(self, ical, raw=None): - if isinstance(ical, icalendar.Event) or isinstance(ical_event, icalendar.Calendar): + if isinstance(ical, icalendar.Event) or isinstance(ical, icalendar.Calendar): ical_event = ical elif hasattr(icalendar.Event, 'from_ical'): ical_event = icalendar.Event.from_ical(ical) @@ -305,7 +355,10 @@ ical = "BEGIN:VCALENDAR\nVERSION:2.0\n" + ical + "\nEND:VCALENDAR" from kolab.calendaring import EventCal self.event = EventCal() - self.event.fromICal(ical) + success = self.event.fromICal(ical) + if success: + self._load_exceptions() + return success def get_attendee_participant_status(self, attendee): return attendee.get_participant_status() @@ -420,6 +473,9 @@ def get_exception_dates(self): return map(lambda _: xmlutils.from_cdatetime(_, True), self.event.exceptionDates()) + def get_exceptions(self): + return self._exceptions + def get_attachments(self): return self.event.attachments() @@ -541,6 +597,9 @@ except: return datetime.datetime.now() + def get_ical_lastmodified(self): + return self.get_ical_dtstamp() + def get_ical_dtstart(self): return self.get_start() @@ -575,6 +634,23 @@ return [ comment ] return None + def get_ical_recurrenceid(self):
View file
pykolab-0.7.8.tar.gz/pykolab/xml/recurrence_rule.py -> pykolab-0.7.9.tar.gz/pykolab/xml/recurrence_rule.py
Changed
@@ -142,6 +142,9 @@ def set_byday(self, bdays): daypos = kolabformat.vectordaypos() for wday in bdays: + if isinstance(wday, str): + wday = icalendar.vWeekday(wday) + weekday = str(wday)[-2:] occurrence = int(wday.relative) if str(wday)[0] == '-': @@ -177,7 +180,11 @@ name_map = dict([(v, k) for (k, v) in map.iteritems()]) return name_map[val] if name_map.has_key(val) else 'UNKNOWN' - def to_dict(self): + def to_ical(self): + rrule = icalendar.vRecur(dict((k,v) for k,v in self.to_dict(True).items() if not (type(v) == str and v == '' or type(v) == list and len(v) == 0))) + return rrule + + def to_dict(self, raw=False): if not self.isValid() or self.frequency() == kolabformat.RecurrenceRule.FreqNone: return None @@ -194,9 +201,12 @@ if isinstance(val, kolabformat.cDateTime): val = xmlutils.from_cdatetime(val, True) elif isinstance(val, kolabformat.vectori): - val = ",".join([int(v) for x in val]) + val = [int(x) for x in val] elif isinstance(val, kolabformat.vectordaypos): - val = ",".join(["%s%s" % (str(x.occurence()) if x.occurence() != 0 else '', self._translate_value(x.weekday(), self.weekday_map)) for x in val]) + val = ["%s%s" % (str(x.occurence()) if x.occurence() != 0 else '', self._translate_value(x.weekday(), self.weekday_map)) for x in val] + + if not raw and isinstance(val, list): + val = ",".join(val) if val is not None: data[p] = val
View file
pykolab-0.7.8.tar.gz/pykolab/xml/todo.py -> pykolab-0.7.9.tar.gz/pykolab/xml/todo.py
Changed
@@ -41,6 +41,7 @@ def __init__(self, from_ical="", from_string=""): self._attendees = [] self._categories = [] + self._exceptions = [] self._attachment_parts = [] self.properties_map.update({
View file
pykolab-0.7.8.tar.gz/pykolab/xml/utils.py -> pykolab-0.7.9.tar.gz/pykolab/xml/utils.py
Changed
@@ -122,6 +122,11 @@ return _cdatetime +def dates_equal(a, b): + date_format = '%Y%m%d' if isinstance(a, datetime.date) and isinstance(b, datetime.date) else '%Y%m%dT%H%M%S' + return type(a) == type(b) and a.strftime(date_format) == b.strftime(date_format) + + property_labels = { "name": N_("Name"), "summary": N_("Summary"), @@ -183,7 +188,7 @@ elif propname == 'rrule': from . import recurrence_rule - rrule = recurrence_rule.frequency_label(value['frequency']) % (value['interval']) + rrule = recurrence_rule.frequency_label(value['freq']) % (value['interval']) if value.has_key('count') and value['count'] > 0: rrule += " " + _("for %d times") % (value['count']) elif value.has_key('until') and (isinstance(value['until'], datetime.datetime) or isinstance(value['until'], datetime.date)):
View file
pykolab-0.7.8.tar.gz/saslauthd/kolab-saslauthd.systemd -> pykolab-0.7.9.tar.gz/saslauthd/kolab-saslauthd.systemd
Changed
@@ -6,7 +6,7 @@ Type=forking PIDFile=/var/run/kolab-saslauthd/kolab-saslauthd.pid EnvironmentFile=/etc/sysconfig/kolab-saslauthd -ExecStart=/usr/sbin/kolab-saslauthd $FLAGS +ExecStart=/usr/sbin/kolab-saslauthd $FLAGS --socket /run/saslauthd/mux ExecReload=/bin/kill -HUP $MAINPID ExecStop=/bin/kill -TERM $MAINPID
View file
pykolab-0.7.8.tar.gz/share/templates/amavisd.conf.tpl -> pykolab-0.7.9.tar.gz/share/templates/amavisd.conf.tpl
Changed
@@ -373,7 +373,7 @@ # \#\## http://www.clamav.net/ ['ClamAV-clamd', - \&ask_daemon, ["CONTSCAN {}\n", "/var/spool/amavisd/clamd.sock"], + \&ask_daemon, ["CONTSCAN {}\n", "$clamdsock"], qr/\bOK\$/m, qr/\bFOUND\$/m, qr/^.*?: (?!Infected Archive)(.*) FOUND\$/m ], # # NOTE: run clamd under the same user as amavisd, or run it under its own
View file
pykolab-0.7.8.tar.gz/share/templates/roundcubemail/config.inc.php.tpl -> pykolab-0.7.9.tar.gz/share/templates/roundcubemail/config.inc.php.tpl
Changed
@@ -68,7 +68,6 @@ 'password', 'redundant_attachments', 'tasklist', - 'threading_as_default', // contextmenu must be after kolab_addressbook (#444) 'contextmenu', );
View file
pykolab-0.7.8.tar.gz/tests/functional/test_wallace/test_005_resource_invitation.py -> pykolab-0.7.9.tar.gz/tests/functional/test_wallace/test_005_resource_invitation.py
Changed
@@ -4,11 +4,13 @@ import email import datetime import uuid +import re from pykolab.imap import IMAP from wallace import module_resources from pykolab.translate import _ +from pykolab.xml import utils as xmlutils from pykolab.xml import event_from_message from pykolab.xml import participant_status_label from pykolab.itip import events_from_message @@ -26,7 +28,7 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/London:%s DTEND;TZID=Europe/London:%s @@ -47,7 +49,7 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP:20140215T125414Z DTSTART;TZID=Europe/London:%s DTEND;TZID=Europe/London:%s @@ -69,7 +71,7 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP;VALUE=DATE-TIME:20140227T141939Z DTSTART;VALUE=DATE-TIME;TZID=Europe/London:%s DTEND;VALUE=DATE-TIME;TZID=Europe/London:%s @@ -94,7 +96,7 @@ CALSCALE:GREGORIAN METHOD:CANCEL BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP:20140218T125414Z DTSTART;TZID=Europe/London:20120713T100000 DTEND;TZID=Europe/London:20120713T110000 @@ -116,7 +118,7 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP:20140213T125414Z DTSTART;VALUE=DATE:%s DTEND;VALUE=DATE:%s @@ -137,10 +139,10 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%s +UID:%s%s DTSTAMP:20140213T125414Z -DTSTART;TZID=Europe/Zurich:%s -DTEND;TZID=Europe/Zurich:%s +DTSTART;TZID=Europe/London:%s +DTEND;TZID=Europe/London:%s RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10 SUMMARY:test DESCRIPTION:test @@ -214,8 +216,8 @@ } from tests.functional.user_add import user_add - user_add("John", "Doe") - user_add("Jane", "Manager") + user_add("John", "Doe", kolabinvitationpolicy='ALL_MANUAL') + user_add("Jane", "Manager", kolabinvitationpolicy='ALL_MANUAL') funcs.purge_resources() self.audi = funcs.resource_add("car", "Audi A4") @@ -242,7 +244,7 @@ smtp.sendmail(from_addr, to_addr, mime_message % (to_addr, itip_payload)) smtp.quit() - def send_itip_invitation(self, resource_email, start=None, allday=False, template=None, uid=None): + def send_itip_invitation(self, resource_email, start=None, allday=False, template=None, uid=None, instance=None): if start is None: start = datetime.datetime.now() @@ -258,8 +260,13 @@ default_template = itip_invitation date_format = '%Y%m%dT%H%M%S' + recurrence_id = '' + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime(date_format) + self.send_message((template if template is not None else default_template) % ( uid, + recurrence_id, start.strftime(date_format), end.strftime(date_format), resource_email @@ -268,13 +275,24 @@ return uid - def send_itip_update(self, resource_email, uid, start=None, template=None): + def send_itip_update(self, resource_email, uid, start=None, template=None, sequence=None, instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) + + recurrence_id = '' + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime('%Y%m%dT%H%M%S') + + if sequence is not None: + if not template: + template = itip_update + template = re.sub(r'SEQUENCE:\d+', 'SEQUENCE:' + str(sequence), template) + self.send_message((template if template is not None else itip_update) % ( uid, + recurrence_id, start.strftime('%Y%m%dT%H%M%S'), end.strftime('%Y%m%dT%H%M%S'), resource_email @@ -283,9 +301,14 @@ return uid - def send_itip_cancel(self, resource_email, uid): + def send_itip_cancel(self, resource_email, uid, instance=None): + recurrence_id = '' + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime('%Y%m%dT%H%M%S') + self.send_message(itip_cancellation % ( uid, + recurrence_id, resource_email ), resource_email) @@ -293,6 +316,21 @@ return uid + def send_owner_response(self, event, partstat, from_addr=None): + if from_addr is None: + from_addr = self.jane['mail'] + + itip_reply = event.to_message_itip(from_addr, + method="REPLY", + participant_status=partstat, + message_text="Request " + partstat, + subject="Booking has been %s" % (partstat) + ) + + smtp = smtplib.SMTP('localhost', 10026) + smtp.sendmail(from_addr, str(event.get_organizer().email()), str(itip_reply)) + smtp.quit() + def check_message_received(self, subject, from_addr=None, mailbox=None): if mailbox is None: mailbox = self.john['mailbox'] @@ -303,7 +341,7 @@ imap.imap.m.select(mailbox) found = None - retries = 10 + retries = 15 while not found and retries > 0: retries -= 1 @@ -322,7 +360,7 @@ return found - def check_resource_calendar_event(self, mailbox, uid=None): + def check_resource_calendar_event(self, mailbox, uid=None, instance=None): imap = IMAP() imap.connect() @@ -345,7 +383,7 @@ continue found = event_from_message(event_message) - if found: + if found and (instance is None or found.is_recurring() or xmlutils.dates_equal(instance, found.get_recurrence_id())): break time.sleep(1) @@ -656,19 +694,9 @@ notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox'])
View file
pykolab-0.7.8.tar.gz/tests/functional/test_wallace/test_007_invitationpolicy.py -> pykolab-0.7.9.tar.gz/tests/functional/test_wallace/test_007_invitationpolicy.py
Changed
@@ -29,7 +29,7 @@ CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT -UID:%(uid)s +UID:%(uid)s%(recurrenceid)s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/Berlin:%(start)s DTEND;TZID=Europe/Berlin:%(end)s @@ -51,7 +51,7 @@ CALSCALE:GREGORIAN METHOD:CANCEL BEGIN:VEVENT -UID:%(uid)s +UID:%(uid)s%(recurrenceid)s DTSTAMP:20140218T125414Z DTSTART;TZID=Europe/Berlin:20120713T100000 DTEND;TZID=Europe/Berlin:20120713T110000 @@ -74,8 +74,8 @@ BEGIN:VEVENT UID:%(uid)s DTSTAMP:20140213T125414Z -DTSTART;TZID=Europe/Zurich:%(start)s -DTEND;TZID=Europe/Zurich:%(end)s +DTSTART;TZID=Europe/Berlin:%(start)s +DTEND;TZID=Europe/Berlin:%(end)s RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10 SUMMARY:%(summary)s DESCRIPTION:test @@ -95,7 +95,7 @@ METHOD:REPLY BEGIN:VEVENT SUMMARY:%(summary)s -UID:%(uid)s +UID:%(uid)s%(recurrenceid)s DTSTART;TZID=Europe/Berlin;VALUE=DATE-TIME:%(start)s DTEND;TZID=Europe/Berlin;VALUE=DATE-TIME:%(end)s DTSTAMP;VALUE=DATE-TIME:20140706T171038Z @@ -115,7 +115,7 @@ METHOD:REPLY BEGIN:VEVENT SUMMARY:%(summary)s -UID:%(uid)s +UID:%(uid)s%(recurrenceid)s DTSTART;TZID=Europe/Berlin;VALUE=DATE-TIME:%(start)s DTEND;TZID=Europe/Berlin;VALUE=DATE-TIME:%(end)s DTSTAMP;VALUE=DATE-TIME:20140706T171038Z @@ -351,11 +351,12 @@ smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(from_addr, to_addr, mime_message % (to_addr, method, itip_payload)) - def send_itip_invitation(self, attendee_email, start=None, allday=False, template=None, summary="test", sequence=0, partstat='NEEDS-ACTION', from_addr=None): + def send_itip_invitation(self, attendee_email, start=None, allday=False, template=None, summary="test", sequence=0, partstat='NEEDS-ACTION', from_addr=None, instance=None): if start is None: start = datetime.datetime.now() uid = str(uuid.uuid4()) + recurrence_id = '' if allday: default_template = itip_allday @@ -372,8 +373,12 @@ else: default_template = default_template.replace("john.doe@example.org", from_addr) + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) + self.send_message((template if template is not None else default_template) % { 'uid': uid, + 'recurrenceid': recurrence_id, 'start': start.strftime(date_format), 'end': end.strftime(date_format), 'mailto': attendee_email, @@ -385,15 +390,23 @@ return uid - def send_itip_update(self, attendee_email, uid, start=None, template=None, summary="test", sequence=1, partstat='ACCEPTED'): + def send_itip_update(self, attendee_email, uid, start=None, template=None, summary="test", sequence=1, partstat='ACCEPTED', instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) + + date_format = '%Y%m%dT%H%M%S' + recurrence_id = '' + + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) + self.send_message((template if template is not None else itip_invitation) % { 'uid': uid, - 'start': start.strftime('%Y%m%dT%H%M%S'), - 'end': end.strftime('%Y%m%dT%H%M%S'), + 'recurrenceid': recurrence_id, + 'start': start.strftime(date_format), + 'end': end.strftime(date_format), 'mailto': attendee_email, 'summary': summary, 'sequence': sequence, @@ -403,15 +416,23 @@ return uid - def send_itip_reply(self, uid, attendee_email, mailto, start=None, template=None, summary="test", sequence=0, partstat='ACCEPTED'): + def send_itip_reply(self, uid, attendee_email, mailto, start=None, template=None, summary="test", sequence=0, partstat='ACCEPTED', instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) + + date_format = '%Y%m%dT%H%M%S' + recurrence_id = '' + + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) + self.send_message((template if template is not None else itip_reply) % { 'uid': uid, - 'start': start.strftime('%Y%m%dT%H%M%S'), - 'end': end.strftime('%Y%m%dT%H%M%S'), + 'recurrenceid': recurrence_id, + 'start': start.strftime(date_format), + 'end': end.strftime(date_format), 'mailto': attendee_email, 'organizer': mailto, 'summary': summary, @@ -424,9 +445,18 @@ return uid - def send_itip_cancel(self, attendee_email, uid, template=None, summary="test", sequence=1): + def send_itip_cancel(self, attendee_email, uid, template=None, summary="test", sequence=1, instance=None, thisandfuture=False): + recurrence_id = '' + + if instance is not None: + recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin%s:%s" % ( + ';RANGE=THISANDFUTURE' if thisandfuture else '', + instance.strftime('%Y%m%dT%H%M%S') + ) + self.send_message((template if template is not None else itip_cancellation) % { 'uid': uid, + 'recurrenceid': recurrence_id, 'mailto': attendee_email, 'summary': summary, 'sequence': sequence, @@ -436,7 +466,7 @@ return uid - def create_calendar_event(self, start=None, summary="test", sequence=0, user=None, attendees=None, folder=None): + def create_calendar_event(self, start=None, summary="test", sequence=0, user=None, attendees=None, folder=None, recurring=False, uid=None): if start is None: start = datetime.datetime.now(pytz.timezone("Europe/Berlin")) if user is None: @@ -453,12 +483,23 @@ event.set_end(end) event.set_organizer(user['mail'], user['displayname']) + if uid: + event.set_uid(uid) + for attendee in attendees: event.add_attendee(attendee['mail'], attendee['displayname'], role="REQ-PARTICIPANT", participant_status="NEEDS-ACTION", rsvp=True) event.set_summary(summary) event.set_sequence(sequence) + if recurring and isinstance(recurring, kolabformat.RecurrenceRule): + event.set_recurrence(rrule) + else: + rrule = kolabformat.RecurrenceRule() + rrule.setFrequency(kolabformat.RecurrenceRule.Daily) + rrule.setCount(10) + event.set_recurrence(rrule) + # create event with attachment vattach = event.get_attachments() attachment = kolabformat.Attachment() @@ -1046,6 +1087,187 @@ self.assertIsInstance(event, pykolab.xml.Event) + def test_015_update_single_occurrence(self): + self.purge_mailbox(self.john['mailbox']) + + start = datetime.datetime(2015,4,2, 14,0,0) + uid = self.send_itip_invitation(self.jane['mail'], start, template=itip_recurring) + + response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + self.assertIsInstance(response, email.message.Message) + + event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + self.assertTrue(event.is_recurring()) + + # send update to a single instance with the same sequence: no re-scheduling + exdate = start + datetime.timedelta(days=14)
View file
pykolab-0.7.8.tar.gz/tests/unit/test-003-event.py -> pykolab-0.7.9.tar.gz/tests/unit/test-003-event.py
Changed
@@ -9,6 +9,7 @@ from pykolab.xml import Attendee from pykolab.xml import Event +from pykolab.xml import RecurrenceRule from pykolab.xml import EventIntegrityError from pykolab.xml import InvalidAttendeeParticipantStatusError from pykolab.xml import InvalidEventDateError @@ -69,6 +70,25 @@ END:VEVENT """ +ical_exception = """ +BEGIN:VEVENT +UID:7a35527d-f783-4b58-b404-b1389bd2fc57 +DTSTAMP;VALUE=DATE-TIME:20140407T122311Z +CREATED;VALUE=DATE-TIME:20140407T122245Z +LAST-MODIFIED;VALUE=DATE-TIME:20140407T122311Z +RECURRENCE-ID;TZID=Europe/Zurich;RANGE=THISANDFUTURE:20140606T110000 +DTSTART;TZID=Europe/Zurich;VALUE=DATE-TIME:20140607T120000 +DTEND;TZID=Europe/Zurich;VALUE=DATE-TIME:20140607T143000 +SUMMARY:Exception +CATEGORIES:Personal +TRANSP:TRANSPARENT +PRIORITY:2 +SEQUENCE:3 +STATUS:CANCELLED +ORGANIZER;CN=Doe\, John:mailto:john.doe@example.org +END:VEVENT +""" + xml_event = """ <icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> <vcalendar> @@ -119,7 +139,7 @@ <recur> <freq>DAILY</freq> <until> - <date>2014-07-25</date> + <date>2015-07-25</date> </until> </recur> </rrule> @@ -241,6 +261,72 @@ </valarm> </components> </vevent> + <vevent> + <properties> + <uid> + <text>75c740bb-b3c6-442c-8021-ecbaeb0a025e</text> + </uid> + <created> + <date-time>2014-07-07T01:28:23Z</date-time> + </created> + <dtstamp> + <date-time>2014-07-07T01:28:23Z</date-time> + </dtstamp> + <sequence> + <integer>2</integer> + </sequence> + <class> + <text>PUBLIC</text> + </class> + <dtstart> + <parameters> + <tzid> + <text>/kolab.org/Europe/London</text> + </tzid> + </parameters> + <date-time>2014-08-16T13:00:00</date-time> + </dtstart> + <dtend> + <parameters> + <tzid><text>/kolab.org/Europe/London</text></tzid> + </parameters> + <date-time>2014-08-16T16:00:00</date-time> + </dtend> + <recurrence-id> + <parameters> + <tzid> + <text>/kolab.org/Europe/London</text> + </tzid> + <range> + <text>THISANDFUTURE</text> + </range> + </parameters> + <date-time>2014-08-16T10:00:00</date-time> + </recurrence-id> + <summary> + <text>exception</text> + </summary> + <description> + <text>exception</text> + </description> + <location> + <text>Room 101</text> + </location> + <organizer> + <parameters> + <cn><text>Doe, John</text></cn> + </parameters> + <cal-address>mailto:%3Cjohn%40example.org%3E</cal-address> + </organizer> + <attendee> + <parameters> + <partstat><text>DECLINED</text></partstat> + <role><text>REQ-PARTICIPANT</text></role> + </parameters> + <cal-address>mailto:%3Cjane%40example.org%3E</cal-address> + </attendee> + </properties> + </vevent> </components> </vcalendar> </icalendar> @@ -319,6 +405,13 @@ def test_009_invalid_participant_status(self): self.assertRaises(InvalidAttendeeParticipantStatusError, self.event.set_attendee_participant_status, "jane@doe.org", "INVALID") + def test_009_update_attendees(self): + jane = self.event.get_attendee("jane@doe.org") + jane.set_name("Jane (GI) Doe") + self.event.update_attendees([jane]) + self.assertEqual(len(self.event.get_attendees()), 2) + self.assertEqual(self.event.get_attendee("jane@doe.org").get_name(), "Jane (GI) Doe") + def test_010_datetime_from_string(self): self.assertRaises(InvalidEventDateError, self.event.set_start, "2012-05-23 11:58:00") @@ -366,7 +459,7 @@ 2.1.3//EN CALSCALE:GREGORIAN METHOD:REQUEST - """ + ical_event + "END:VCALENDAR" + """ + ical_event + ical_exception + "END:VCALENDAR" ical = icalendar.Calendar.from_ical(ical_str) event = event_from_ical(ical.walk('VEVENT')[0], ical_str) @@ -389,6 +482,12 @@ self.assertIsInstance(event.get_exception_dates()[0], datetime.datetime) self.assertEqual(len(event.get_alarms()), 1) self.assertEqual(len(event.get_attachments()), 2) + self.assertEqual(len(event.get_exceptions()), 1) + + exception = event.get_exceptions()[0] + self.assertIsInstance(exception.get_recurrence_id(), datetime.datetime) + # self.assertEqual(exception.thisandfuture, True) + self.assertEqual(str(exception.get_start()), "2014-06-07 12:00:00+02:00") def test_018_ical_to_message(self): event = event_from_ical(ical_event) @@ -436,6 +535,15 @@ self.event.set_sequence(3) self.event.set_classification('CONFIDENTIAL') self.event.add_custom_property('X-Custom', 'check') + self.event.set_recurrence_id(datetime.datetime(2014, 05, 23, 11, 0, 0), True) + + rrule = RecurrenceRule() + rrule.set_frequency(kolabformat.RecurrenceRule.Weekly) + rrule.set_byday(['2WE','-1SU']) + rrule.setBymonth([2]) + rrule.set_count(10) + rrule.set_until(datetime.datetime(2014,7,23, 11,0,0, tzinfo=pytz.utc)) + self.event.set_recurrence(rrule); ical = icalendar.Calendar.from_ical(self.event.as_string_itip()) event = ical.walk('VEVENT')[0] @@ -446,6 +554,16 @@ self.assertEqual(event['X-CUSTOM'], "check") self.assertIsInstance(event['dtstamp'].dt, datetime.datetime) self.assertEqual(event['class'], "CONFIDENTIAL") + self.assertIsInstance(event['recurrence-id'].dt, datetime.datetime) + self.assertEqual(event['recurrence-id'].params.get('RANGE'), 'THISANDFUTURE') + + self.assertTrue(event.has_key('rrule')) + self.assertEqual(event['rrule']['FREQ'][0], 'WEEKLY') + self.assertEqual(event['rrule']['INTERVAL'][0], 1) + self.assertEqual(event['rrule']['COUNT'][0], 10) + self.assertEqual(event['rrule']['BYMONTH'][0], 2) + self.assertEqual(event['rrule']['BYDAY'], ['2WE','-1SU']) + self.assertIsInstance(event['rrule']['UNTIL'][0], datetime.datetime) def test_019_to_message_itip(self): self.event = Event() @@ -529,6 +647,7 @@ # check get_next_instance() which returns a clone of the base event next_instance = self.event.get_next_instance(next_date) self.assertIsInstance(next_instance, Event) + self.assertIsInstance(next_instance.get_recurrence_id(), datetime.datetime) self.assertEqual(self.event.get_summary(), next_instance.get_summary()) self.assertEqual(next_instance.get_start().month, 7) self.assertFalse(next_instance.is_recurring()) @@ -554,6 +673,50 @@ self.assertEqual(self.event.get_next_occurence(_start), None) self.assertEqual(self.event.get_last_occurrence(), None) + def test_021_add_exceptions(self): + event = event_from_ical(ical_event) + exception = event_from_ical(ical_exception)
View file
pykolab-0.7.8.tar.gz/tests/unit/test-011-itip.py -> pykolab-0.7.9.tar.gz/tests/unit/test-011-itip.py
Changed
@@ -451,6 +451,31 @@ event4.set_end(datetime.datetime(2012,7,1, 10,30,0, tzinfo=pytz.utc)) self.assertFalse(itip.check_event_conflict(event4, itip_event), "No conflict in two recurring events") + itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] + + rrule.setFrequency(kolabformat.RecurrenceRule.Daily) + rrule.setCount(10) + + event5 = Event() + event5.set_recurrence(rrule); + event5.set_start(datetime.datetime(2012,7,9, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) + event5.set_end(datetime.datetime(2012,7,9, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) + + event_xml = str(event5) + exception = Event(from_string=event_xml) + exception.set_start(datetime.datetime(2012,7,13, 14,0,0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_end(datetime.datetime(2012,7,13, 16,0,0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) + event5.add_exception(exception) + self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with exception date") + + exception = Event(from_string=event_xml) + exception.set_start(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_end(datetime.datetime(2012,7,13, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_status('CANCELLED') + exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) + event5.add_exception(exception) + self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with cancelled exception") def test_003_send_reply(self): itip_events = itip.events_from_message(message_from_string(itip_non_multipart))
View file
pykolab-0.7.8.tar.gz/wallace/module_invitationpolicy.py -> pykolab-0.7.9.tar.gz/wallace/module_invitationpolicy.py
Changed
@@ -18,6 +18,7 @@ # import datetime +import pytz import os import tempfile import time @@ -25,6 +26,7 @@ import urllib import hashlib import traceback +import re from email import message_from_string from email.parser import Parser @@ -398,7 +400,7 @@ condition_fulfilled = True # find existing event in user's calendar - existing = find_existing_object(itip_event['uid'], itip_event['type'], receiving_user, True) + (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True) # compare sequence number to determine a (re-)scheduling request if existing is not None: @@ -454,7 +456,7 @@ receiving_attendee.set_name(receiving_user['cn']) # send iTip reply - if respond_with is not None: + if respond_with is not None and not respond_with == 'NEEDS-ACTION': receiving_attendee.set_participant_status(respond_with) send_reply(recipient_email, itip_event, invitation_response_text(itip_event['type']), subject=_('"%(summary)s" has been %(status)s')) @@ -478,7 +480,7 @@ if not nonpart or existing: # save new copy from iTip - if store_object(itip_event['xml'], receiving_user, targetfolder): + if store_object(itip_event['xml'], receiving_user, targetfolder, master): if policy & COND_FORWARD: log.debug(_("Forward invitation for notification"), level=5) return MESSAGE_FORWARD @@ -509,7 +511,7 @@ # find existing event in user's calendar # sets/checks lock to avoid concurrent wallace processes trying to update the same event simultaneously - existing = find_existing_object(itip_event['uid'], itip_event['type'], receiving_user, True) + (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True) if existing: # compare sequence number to avoid outdated replies? @@ -521,16 +523,18 @@ return MESSAGE_FORWARD log.debug(_("Auto-updating %s %r on iTip REPLY") % (existing.type, existing.uid), level=8) + updated_attendees = [] try: - existing_attendee = existing.get_attendee(sender_email) existing.set_attendee_participant_status(sender_email, sender_attendee.get_participant_status(), rsvp=False) + existing_attendee = existing.get_attendee(sender_email) + updated_attendees.append(existing_attendee) except Exception, e: log.error("Could not find corresponding attende in organizer's copy: %r" % (e)) # append delegated-from attendee ? if len(sender_attendee.get_delegated_from()) > 0: - existing._attendees.append(sender_attendee) - existing.event.setAttendees(existing._attendees) + existing.add_attendee(sender_attendee) + updated_attendees.append(sender_attendee) else: # TODO: accept new participant if ACT_ACCEPT ? remove_write_lock(existing._lock_key) @@ -544,28 +548,30 @@ existing_delegatee = existing.find_attendee(delegatee_email) if not existing_delegatee: - existing._attendees.append(sender_delegatee) + existing.add_attendee(sender_delegatee) log.debug(_("Add delegatee: %r") % (sender_delegatee.to_dict()), level=9) else: existing_delegatee.copy_from(sender_delegatee) log.debug(_("Update existing delegatee: %r") % (existing_delegatee.to_dict()), level=9) + updated_attendees.append(sender_delegatee) + # copy all parameters from replying attendee (e.g. delegated-to, role, etc.) existing_attendee.copy_from(sender_attendee) - existing.event.setAttendees(existing._attendees) + existing.update_attendees([existing_attendee]) log.debug(_("Update delegator: %r") % (existing_attendee.to_dict()), level=9) except Exception, e: log.error("Could not find delegated-to attendee: %r" % (e)) # update the organizer's copy of the object - if update_object(existing, receiving_user): + if update_object(existing, receiving_user, master): if policy & COND_NOTIFY: send_update_notification(existing, receiving_user, existing, True) # update all other attendee's copies if conf.get('wallace','invitationpolicy_autoupdate_other_attendees_on_reply'): - propagate_changes_to_attendees_accounts(existing) + propagate_changes_to_attendees_accounts(existing, updated_attendees) return MESSAGE_PROCESSED @@ -589,12 +595,20 @@ # auto-update the local copy with STATUS=CANCELLED if policy & ACT_UPDATE: # find existing object in user's folders - existing = find_existing_object(itip_event['uid'], itip_event['type'], receiving_user, True) + (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True) if existing: + # on this-and-future cancel requests, set the recurrence until date on the master event + if itip_event['recurrence-id'] and master and itip_event['xml'].get_thisandfuture(): + rrule = master.get_recurrence() + rrule.set_count(0) + rrule.set_until(existing.get_start().astimezone(pytz.utc) + datetime.timedelta(days=-1)) + master.set_recurrence(rrule) + existing.set_recurrence_id(existing.get_recurrence_id(), True) + existing.set_status('CANCELLED') existing.set_transparency(True) - if update_object(existing, receiving_user): + if update_object(existing, receiving_user, master): # send cancellation notification if policy & ACT_UPDATE_AND_NOTIFY: send_cancel_notification(existing, receiving_user) @@ -602,7 +616,7 @@ return MESSAGE_PROCESSED else: - log.error(_("The object referred by this reply was not found in the user's folders. Forwarding to Inbox.")) + log.error(_("The object referred by this cancel request was not found in the user's folders. Forwarding to Inbox.")) return MESSAGE_FORWARD return None @@ -735,7 +749,7 @@ for folder in folders: # exclude shared and other user's namespace - if not ns_other is None and folder.startswith(ns_other): + if not ns_other is None and folder.startswith(ns_other) and user_rec.has_key('_delegated_mailboxes'): # allow shared folders from delegators if len([_mailbox for _mailbox in user_rec['_delegated_mailboxes'] if folder.startswith(ns_other + _mailbox + '/')]) == 0: continue; @@ -765,7 +779,7 @@ return result -def find_existing_object(uid, type, user_rec, lock=False): +def find_existing_object(uid, type, recurrence_id, user_rec, lock=False): """ Search user's private folders for the given object (by UID+type) """ @@ -778,13 +792,20 @@ set_write_lock(lock_key) event = None + master = None for folder in list_user_folders(user_rec, type): log.debug(_("Searching folder %r for %s %r") % (folder, type, uid), level=8) imap.imap.m.select(imap.folder_utf7(folder)) - typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) + res, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) for num in reversed(data[0].split()): - typ, data = imap.imap.m.fetch(num, '(RFC822)') + res, data = imap.imap.m.fetch(num, '(UID RFC822)') + + try: + msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) + except Exception, e: + log.error(_("No UID found in IMAP response: %r") % (data[0][0])) + continue try: if type == 'task': @@ -792,19 +813,35 @@ else: event = event_from_message(message_from_string(data[0][1])) + # find instance in a recurring series + if recurrence_id and event.is_recurring(): + master = event + event = master.get_instance(recurrence_id) + setattr(master, '_imap_folder', folder) + setattr(master, '_msguid', msguid) + + # compare recurrence-id and skip to next message if not matching + elif recurrence_id and not event.is_recurring() and not xmlutils.dates_equal(recurrence_id, event.get_recurrence_id()): + log.debug(_("Recurrence-ID not matching on message %s, skipping: %r != %r") % ( + msguid, recurrence_id, event.get_recurrence_id() + ), level=8) + continue +
View file
pykolab-0.7.8.tar.gz/wallace/module_resources.py -> pykolab-0.7.9.tar.gz/wallace/module_resources.py
Changed
@@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2015 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -44,6 +44,7 @@ from pykolab.conf import Conf from pykolab.imap import IMAP from pykolab.xml import to_dt +from pykolab.xml import utils as xmlutils from pykolab.xml import event_from_message from pykolab.xml import participant_status_label from pykolab.itip import events_from_message @@ -265,14 +266,16 @@ # find initial reservation referenced by the reply if reference_uid: - event = find_existing_event(reference_uid, receiving_resource) + (event, master) = find_existing_event(reference_uid, itip_event['recurrence-id'], receiving_resource) + log.debug(_("iTip REPLY to %r, %r; matches %r") % (reference_uid, itip_event['recurrence-id'], type(event)), level=8) + if event: try: sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) owner_reply = sender_attendee.get_participant_status() log.debug(_("Sender Attendee: %r => %r") % (sender_attendee, owner_reply), level=9) except Exception, e: - log.error("Could not find envelope sender attendee: %r" % (e)) + log.error(_("Could not find envelope sender attendee: %r") % (e)) continue # compare sequence number to avoid outdated replies @@ -287,18 +290,16 @@ if comment: event.set_comment(str(comment)) - itip_event_ = dict(xml=event, uid=event.get_uid()) + _itip_event = dict(xml=event, uid=event.get_uid(), _master=master) + _itip_event['recurrence-id'] = event.get_recurrence_id() if owner_reply == kolabformat.PartAccepted: event.set_status(kolabformat.StatusConfirmed) - accept_reservation_request(itip_event_, receiving_resource, confirmed=True) + accept_reservation_request(_itip_event, receiving_resource, confirmed=True) elif owner_reply == kolabformat.PartDeclined: - decline_reservation_request(itip_event_, receiving_resource) - # TODO: set status=CANCELLED instead of deleting? - # event.set_status(kolabformat.StatusCancelled) - delete_resource_event(reference_uid, receiving_resource) + decline_reservation_request(_itip_event, receiving_resource) else: - log.info("Invalid response (%r) recieved from resource owner for event %r" % ( + log.info(_("Invalid response (%r) recieved from resource owner for event %r") % ( sender_attendee.get_participant_status(True), reference_uid )) else: @@ -316,7 +317,7 @@ receiving_attendee = itip_event['xml'].get_attendee_by_email(receiving_resource['mail']) log.debug(_("Receiving Resource: %r; %r") % (receiving_resource, receiving_attendee), level=9) except Exception, e: - log.error("Could not find envelope attendee: %r" % (e)) + log.error(_("Could not find envelope attendee: %r") % (e)) continue # ignore updates and cancellations to resource collections who already delegated the event @@ -329,7 +330,19 @@ for resource in resource_dns: if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()] \ and resources[resource].has_key('kolabtargetfolder'): - delete_resource_event(itip_event['uid'], resources[resource]) + (event, master) = find_existing_event(itip_event['uid'], itip_event['recurrence-id'], resources[resource]) + # remove entire event + if event and master is None: + log.debug(_("Cancellation for entire event %r: deleting") % (itip_event['uid']), level=8) + delete_resource_event(itip_event['uid'], resources[resource], event._msguid) + # just cancel one single occurrence: add exception with status=cancelled + elif master and master.is_recurring(): + log.debug(_("Cancellation for a single occurrence %r of %r: updating...") % (itip_event['recurrence-id'], itip_event['uid']), level=8) + event.set_status('CANCELLED') + event.set_transparency(True) + _itip_event = dict(xml=event, uid=event.get_uid(), _master=master) + _itip_event['recurrence-id'] = event.get_recurrence_id() + save_resource_event(_itip_event, resources[resource]) done = True @@ -345,11 +358,6 @@ # accept reservation if available_resource is not None: if available_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: - # replace existing copy of this event - if len(available_resource['existing_events']) > 0: - for uid in available_resource['existing_events']: - delete_resource_event(uid, available_resource) - log.debug(_("Accept invitation for individual resource %r / %r") % (available_resource['dn'], available_resource['mail']), level=8) # check if reservation was delegated @@ -557,6 +565,10 @@ # This is the event being conflicted with! for itip_event in itip_events: + # do not re-assign single occurrences to another resource + if itip_event['recurrence-id'] is not None: + continue + # Now we have the event that was conflicting if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: # this resource initially was delegated from a collection ? @@ -583,8 +595,8 @@ # remove existing_events as we now delegated back to the collection if len(resources[resource]['existing_events']) > 0: - for uid in resources[resource]['existing_events']: - delete_resource_event(uid, resources[resource]) + for existing in resources[resource]['existing_events']: + delete_resource_event(existing.uid, resources[resource], existing._msguid) done = True @@ -655,7 +667,13 @@ level=9 ) - typ, data = imap.imap.m.fetch(num, '(RFC822)') + typ, data = imap.imap.m.fetch(num, '(UID RFC822)') + + try: + msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) + except Exception, e: + log.error(_("No UID found in IMAP response: %r") % (data[0][0])) + continue event_message = message_from_string(data[0][1]) @@ -669,8 +687,12 @@ for itip in itip_events: conflict = check_event_conflict(event, itip) - if event.get_uid() == itip['uid']: - resource_rec['existing_events'].append(itip['uid']) + if event.get_uid() == itip['uid'] and (event.is_recurring() or itip['recurrence-id'] == event.get_recurrence_id()): + setattr(event, '_msguid', msguid) + if event.is_recurring(): + resource_rec['existing_master'] = event + else: + resource_rec['existing_events'].append(event) if conflict: log.info( @@ -686,13 +708,14 @@ return num_messages -def find_existing_event(uid, resource_rec): +def find_existing_event(uid, recurrence_id, resource_rec): """ Search the resources's calendar folder for the given event (by UID) """ global imap event = None + master = None mailbox = resource_rec['kolabtargetfolder'] log.debug(_("Searching %r for event %r") % (mailbox, uid), level=9) @@ -705,18 +728,39 @@ return event for num in reversed(data[0].split()): - typ, data = imap.imap.m.fetch(num, '(RFC822)') + typ, data = imap.imap.m.fetch(num, '(UID RFC822)') + + try: + msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) + except Exception, e: + log.error(_("No UID found in IMAP response: %r") % (data[0][0])) + continue try: event = event_from_message(message_from_string(data[0][1])) + + # find instance in a recurring series + if recurrence_id and event.is_recurring(): + master = event + event = master.get_instance(recurrence_id) + setattr(master, '_msguid', msguid) + + # compare recurrence-id and skip to next message if not matching + elif recurrence_id and not event.is_recurring() and not xmlutils.dates_equal(recurrence_id, event.get_recurrence_id()): + log.debug(_("Recurrence-ID not matching on message %s, skipping: %r != %r") % ( + msguid, recurrence_id, event.get_recurrence_id() + ), level=8) + continue + setattr(event, '_msguid', msguid) + except Exception, e: log.error(_("Failed to parse event from message %s/%s: %r") % (mailbox, num, e))
View file
pykolab.dsc
Changed
@@ -2,7 +2,7 @@ Source: pykolab Binary: pykolab, kolab-cli, kolab-conf, kolab-saslauthd, kolab-server, kolab-telemetry, kolab-xml, wallace Architecture: all -Version: 0.7.8-0~kolab3 +Version: 0.7.9-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org @@ -40,5 +40,5 @@ pykolab deb python optional wallace deb python optional Files: - 00000000000000000000000000000000 0 pykolab-0.7.8.tar.gz + 00000000000000000000000000000000 0 pykolab-0.7.9.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.