Projects
Kolab:3.4
pykolab
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 140
View file
pykolab.spec
Changed
@@ -28,16 +28,13 @@ Summary: Kolab Groupware Solution Name: pykolab -Version: 0.7.7 +Version: 0.7.8 Release: 1%{?dist} License: GPLv3+ Group: Applications/System URL: http://kolab.org/ Source0: http://files.kolab.org/releases/%{name}-%{version}.tar.gz -Patch0: fix_mariadb_centos7.patch -Patch1: fix_disrvadmin_centos7.patch -Patch2: fix_chameleon_roundcube_skin.patch Patch3: fix_run_kolabd_centos7.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root @@ -203,10 +200,9 @@ %prep %setup -q -%patch0 -p1 -%patch1 -p1 -%patch2 -p1 +%if 0%{?with_systemd} %patch3 -p1 +%endif %build autoreconf -v @@ -530,6 +526,11 @@ %attr(0700,%{kolab_user},%{kolab_group}) %dir %{_var}/spool/pykolab/wallace %changelog +* Sat Feb 14 2015 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.7.8-1 +- Release of version 0.7.8, see; + + https://issues.kolab.org/buglist.cgi?target_milestone=0.7.8&product=pykolab + * Thu Feb 12 2015 Timotheus Pokorra (TBits.net) <tp@tbits.net> - fix for CentOS7, start service mariadb instead of mysqld. fixing #3877 - use /run/kolabd for the pid, fixing #2626
View file
fix_chameleon_roundcube_skin.patch
Deleted
@@ -1,26 +0,0 @@ -From 8ac49b0318ff98c1107aae16484a36098ffeb2f0 Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Thu, 12 Feb 2015 17:30:03 +0100 -Subject: [PATCH] use chameleon skin for roundcube if it exists - fixes #4557 - ---- - pykolab/setup/setup_roundcube.py | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py -index 739da11..6558989 100644 ---- a/pykolab/setup/setup_roundcube.py -+++ b/pykolab/setup/setup_roundcube.py -@@ -90,7 +90,7 @@ def execute(*args, **kw): - - if os.access('/usr/share/roundcubemail/skins/enterprise/', os.R_OK): - rc_settings['skin'] = 'enterprise' -- elif os.access('/usr/share/roundcubemail/skin/chameleon/', os.R_OK): -+ elif os.access('/usr/share/roundcubemail/skins/chameleon/', os.R_OK): - rc_settings['skin'] = 'chameleon' - else: - rc_settings['skin'] = 'larry' --- -1.7.1 -
View file
fix_disrvadmin_centos7.patch
Deleted
@@ -1,29 +0,0 @@ -From 5c2b539828cab0e83dca72e40ccb2f296dd5611a Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Thu, 12 Feb 2015 16:30:53 +0100 -Subject: [PATCH 2/2] show meaningful error message if dirsrv-admin.service - does not exist fixes #4554 - ---- - pykolab/setup/setup_ldap.py | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py -index 04311d2..c805d20 100644 ---- a/pykolab/setup/setup_ldap.py -+++ b/pykolab/setup/setup_ldap.py -@@ -670,7 +670,10 @@ ServerAdminPwd = %(admin_pass)s - auth._auth.ldap.modify_s(dn, modlist) - - if os.path.isfile('/bin/systemctl'): -- subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service']) -+ if not os.path.isfile('/usr/lib/systemd/system/dirsrv-admin.service'): -+ log.info(_("directory server admin service not available")) -+ else: -+ subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service']) - elif os.path.isfile('/sbin/chkconfig'): - subprocess.call(['/sbin/chkconfig', 'dirsrv-admin', 'on']) - elif os.path.isfile('/usr/sbin/update-rc.d'): --- -1.8.3.1 -
View file
fix_mariadb_centos7.patch
Deleted
@@ -1,41 +0,0 @@ -From 29431733f10e452a4291e626dd0f0c9c72b21753 Mon Sep 17 00:00:00 2001 -From: Timotheus Pokorra <tp@tbits.net> -Date: Thu, 12 Feb 2015 15:17:51 +0100 -Subject: [PATCH] on CentOS7, there is MariaDB instead of MySQL. fixing bug - 3877 - ---- - pykolab/setup/setup_mysql.py | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/pykolab/setup/setup_mysql.py b/pykolab/setup/setup_mysql.py -index 2211876..174cbc7 100644 ---- a/pykolab/setup/setup_mysql.py -+++ b/pykolab/setup/setup_mysql.py -@@ -39,8 +39,13 @@ def description(): - return _("Setup MySQL.") - - def execute(*args, **kw): -+ # on CentOS7, there is MariaDB instead of MySQL -+ mysqlservice = 'mysqld.service' -+ if os.path.isfile('/usr/lib/systemd/system/mariadb.service'): -+ mysqlservice = 'mariadb.service' -+ - if os.path.isfile('/bin/systemctl'): -- subprocess.call(['/bin/systemctl', 'restart', 'mysqld.service']) -+ subprocess.call(['/bin/systemctl', 'restart', mysqlservice]) - elif os.path.isfile('/sbin/service'): - subprocess.call(['/sbin/service', 'mysqld', 'restart']) - elif os.path.isfile('/usr/sbin/service'): -@@ -49,7 +54,7 @@ def execute(*args, **kw): - log.error(_("Could not start the MySQL database service.")) - - if os.path.isfile('/bin/systemctl'): -- subprocess.call(['/bin/systemctl', 'enable', 'mysqld.service']) -+ subprocess.call(['/bin/systemctl', 'enable', mysqlservice]) - elif os.path.isfile('/sbin/chkconfig'): - subprocess.call(['/sbin/chkconfig', 'mysqld', 'on']) - elif os.path.isfile('/usr/sbin/update-rc.d'): --- -1.8.3.1 -
View file
setup_roundcube.patch
Deleted
@@ -1,32 +0,0 @@ -From 8b2933d7bdc2d410691fbc5a39a74a02166610c6 Mon Sep 17 00:00:00 2001 -From: Daniel Hoffend <dh@dotlan.net> -Date: Sun, 14 Sep 2014 02:18:04 +0200 -Subject: [PATCH] fixed import of roundcube mysql.initial.sql - ---- - pykolab/setup/setup_roundcube.py | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py -index 259f676..ab73861 100644 ---- a/pykolab/setup/setup_roundcube.py -+++ b/pykolab/setup/setup_roundcube.py -@@ -140,12 +140,13 @@ def execute(*args, **kw): - - schema_files = [] - for root, directories, filenames in os.walk('/usr/share/doc/'): -+ directories.sort() - for directory in directories: - if directory.startswith("roundcubemail"): -- for root, directories, filenames in os.walk(os.path.join(root, directory)): -- for filename in filenames: -+ for nested_root, nested_directories, nested_filenames in os.walk(os.path.join(root, directory)): -+ for filename in nested_filenames: - if filename.startswith('mysql.initial') and filename.endswith('.sql'): -- schema_filepath = os.path.join(root,filename) -+ schema_filepath = os.path.join(nested_root,filename) - if not schema_filepath in schema_files: - schema_files.append(schema_filepath) - --- -1.7.10.4
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +pykolab (0.7.8-0~kolab1) unstable; urgency=low + + * https://issues.kolab.org/buglist.cgi?target_milestone=0.7.8&product=pykolab + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Sat, 14 Feb 2015 01:49:00 +0100 + pykolab (0.7.7-0~kolab2) unstable; urgency=low * use chameleon skin as default. fixing #4557
View file
debian.series
Changed
@@ -1,3 +1,1 @@ cyrus-imapd.conf-cert-paths.patch -p1 -setup_roundcube.patch -p1 -fix_chameleon_roundcube_skin.patch -p1
View file
pykolab-0.7.7.tar.gz/conf/kolab.conf -> pykolab-0.7.8.tar.gz/conf/kolab.conf
Changed
@@ -412,9 +412,13 @@ footer_text = /etc/kolab/footer.text footer_html = /etc/kolab/footer.html -; default settings for kolabInvitationPolicy +; default settings for kolabInvitationPolicy LDAP attribute on user records kolab_invitation_policy = ACT_ACCEPT_IF_NO_CONFLICT:example.org, ACT_MANUAL +; automatically update the participant status from iTip REPLY messages +; in the personal calendars of all other listed participants. +invitationpolicy_autoupdate_other_attendees_on_reply = false + ; number of days past their end resource calendar events should be kept resource_calendar_expire_days = 100
View file
pykolab-0.7.7.tar.gz/configure.ac -> pykolab-0.7.8.tar.gz/configure.ac
Changed
@@ -1,4 +1,4 @@ -AC_INIT([pykolab], 0.7.7) +AC_INIT([pykolab], 0.7.8) AC_SUBST([RELEASE], 1) AC_CONFIG_SRCDIR(pykolab/constants.py.in)
View file
pykolab-0.7.7.tar.gz/kolabd/kolabd.systemd -> pykolab-0.7.8.tar.gz/kolabd/kolabd.systemd
Changed
@@ -4,11 +4,11 @@ [Service] Type=forking -PIDFile=/var/run/kolabd/kolabd.pid +PIDFile=/run/kolabd/kolabd.pid User=kolab Group=kolab EnvironmentFile=/etc/sysconfig/kolabd -ExecStart=/usr/sbin/kolabd $FLAGS +ExecStart=/usr/sbin/kolabd $FLAGS --pid-file /run/kolabd/kolabd.pid ExecReload=/bin/kill -HUP $MAINPID ExecStop=/bin/kill -TERM $MAINPID
View file
pykolab-0.7.7.tar.gz/pykolab/auth/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/auth/__init__.py
Changed
@@ -222,6 +222,9 @@ def extract_recipient_addresses(self, entry): return self._auth.extract_recipient_addresses(entry) + def list_delegators(self, user): + return self._auth.list_delegators(user) + def list_domains(self, domain=None): """ List the domains using the auth_mechanism setting in the kolab
View file
pykolab-0.7.7.tar.gz/pykolab/auth/ldap/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/auth/ldap/__init__.py
Changed
@@ -429,6 +429,25 @@ return recipient_addresses + def list_delegators(self, entry_id): + """ + Get a list of user records the given user is set to be a delegatee + """ + delegators = [] + + mailbox_attribute = conf.get('cyrus-sasl', 'result_attribute') + if mailbox_attribute == None: + mailbox_attribute = 'mail' + + for __delegator in self.search_entry_by_attribute('kolabDelegate', entry_id): + (_dn, _delegator) = __delegator + _delegator['dn'] = _dn; + _delegator['_mailbox_basename'] = _delegator[mailbox_attribute] if _delegator.has_key(mailbox_attribute) else None + if isinstance(_delegator['_mailbox_basename'], list): + _delegator['_mailbox_basename'] = _delegator['_mailbox_basename'][0] + delegators.append(_delegator) + + return delegators def find_recipient(self, address="*", exclude_entry_id=None): """
View file
pykolab-0.7.7.tar.gz/pykolab/cli/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/cli/__init__.py
Changed
@@ -24,6 +24,8 @@ import shutil import sys import time +import codecs +import locale from ldap.modlist import addModlist @@ -61,6 +63,9 @@ for cmd_component in to_execute: sys.argv.pop(sys.argv.index(cmd_component.replace('_','-'))) + # wrap sys.stdout in a locale-aware StreamWriter (#3983) + sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout) + commands.execute('_'.join(to_execute)) def run(self):
View file
pykolab-0.7.7.tar.gz/pykolab/cli/cmd_list_mailbox_metadata.py -> pykolab-0.7.8.tar.gz/pykolab/cli/cmd_list_mailbox_metadata.py
Changed
@@ -83,7 +83,7 @@ metadata = [] folders = imap.list_folders(folder) for folder in folders: - print "Folder", folder.encode('utf-8') + print "Folder", folder metadata = imap.get_metadata(folder)
View file
pykolab-0.7.7.tar.gz/pykolab/cli/cmd_list_mailboxes.py -> pykolab-0.7.8.tar.gz/pykolab/cli/cmd_list_mailboxes.py
Changed
@@ -89,6 +89,6 @@ for folder in folders: if not conf.raw: - print imap_utf7.decode(folder).encode('utf-8') + print imap_utf7.decode(folder) else: print folder
View file
pykolab-0.7.7.tar.gz/pykolab/cli/cmd_set_quota.py -> pykolab-0.7.8.tar.gz/pykolab/cli/cmd_set_quota.py
Changed
@@ -17,6 +17,8 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +import sys + import commands import pykolab @@ -58,7 +60,7 @@ print >> sys.stderr, _("No such folder %r") % (folder) sys.exit(1) - for _folder in imap.lm(folder): + for _folder in imap.lm(imap.folder_utf7(folder)): imap.set_quota(_folder, quota) - print >> sys.stdout, "Quota for folder '%s' set to %d" % (_folder, quota) + print >> sys.stdout, "Quota for folder '%s' set to %d" % (_folder, int(quota))
View file
pykolab-0.7.7.tar.gz/pykolab/imap/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/imap/__init__.py
Changed
@@ -319,27 +319,23 @@ _namespaces = re.split(r"\)\)\s\(\(", _namespaces) - _other_users = [ - ''.join(_namespaces[1].replace('((','').replace('))','').split()[-1]) - ] - if len(_namespaces) >= 3: _shared = [] - _shared.append(' '.join(_namespaces[2].replace('((','').replace('))','').split()[:-1])) + _shared.append(' '.join(_namespaces[2].replace('((','').replace('))','').split()[:-1]).replace('"', '')) if len(_namespaces) >= 2: - _other_users = ' '.join(_namespaces[1].replace('((','').replace('))','').split()[:-1]) + _other_users = ' '.join(_namespaces[1].replace('((','').replace('))','').split()[:-1]).replace('"', '') if len(_namespaces) >= 1: - _personal = _namespaces[0].replace('((','').replace('))','').split()[0] + _personal = _namespaces[0].replace('((','').replace('))','').split()[0].replace('"', '') - return (_personal.replace('"', ''), _other_users.replace('"', ''), [x.replace('"', '') for x in _shared]) + return (_personal, _other_users, _shared) def set_acl(self, folder, identifier, acl): """ Set an ACL entry on a folder. """ - _acl = [] + _acl = '' short_rights = { 'all': 'lrsedntxakcpiw',
View file
pykolab-0.7.7.tar.gz/pykolab/itip/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/itip/__init__.py
Changed
@@ -115,9 +115,9 @@ try: # distinguish event and todo here if itip['type'] == 'task': - itip['xml'] = todo_from_ical(c.to_ical()) + itip['xml'] = todo_from_ical(c, itip_payload) else: - itip['xml'] = event_from_ical(c.to_ical()) + itip['xml'] = event_from_ical(c, itip_payload) except Exception, e: log.error("event|todo_from_ical() exception: %r; iCal: %s" % (e, itip_payload)) continue
View file
pykolab-0.7.7.tar.gz/pykolab/setup/setup_freebusy.py -> pykolab-0.7.8.tar.gz/pykolab/setup/setup_freebusy.py
Changed
@@ -68,18 +68,24 @@ if hasattr(result, 'hostname'): hostname = result.hostname + + if hasattr(result, 'port'): + port = result.port + + if hasattr(result, 'scheme'): + scheme = result.scheme + else: scheme = imap_uri.split(':')[0] (hostname, port) = imap_uri.split('/')[2].split(':') - if port == None: + if scheme == 'imaps' and (port == None or port == ''): port = 993 - if scheme == None or scheme == "": - scheme = 'imaps' + if scheme == None or scheme == '': + scheme = 'imap' - if scheme == "imaps" and port == 993: - scheme = "imap" + if port == None or port == '': port = 143 resources_imap_uri = '%s://%s:%s@%s:%s/%%kolabtargetfolder?acl=lrs' % (scheme, admin_login, admin_password, hostname, port) @@ -104,6 +110,7 @@ 'filter': '(&(objectClass=kolabInetOrgPerson)(|(mail=%s)(alias=%s)))', 'attributes': 'mail', 'lc_attributes': 'mail', + 'primary_domain': conf.get('kolab', 'primary_domain'), 'fbsource': users_imap_uri, 'cacheto': '/var/cache/kolab-freebusy/%s.ifb', 'expires': '15m', @@ -117,11 +124,29 @@ 'bind_pw': conf.get('ldap', 'service_bind_pw'), 'attributes': 'mail, kolabtargetfolder', 'filter': '(&(objectClass=kolabsharedfolder)(kolabfoldertype=event)(mail=%s))', + 'primary_domain': conf.get('kolab', 'primary_domain'), 'fbsource': resources_imap_uri, 'cacheto': '/var/cache/kolab-freebusy/%s.ifb', 'expires': '15m', 'loglevel': 300, }, + 'directory "kolab-resource-collections"': { + 'type': 'ldap', + 'host': conf.get('ldap', 'ldap_uri'), + 'base_dn': conf.get('ldap', 'resource_base_dn'), + 'bind_dn': conf.get('ldap', 'service_bind_dn'), + 'bind_pw': conf.get('ldap', 'service_bind_pw'), + 'filter': '(&(objectClass=kolabgroupofuniquenames)(mail=%s))', + 'attributes': 'uniquemember', 'mail' + 'resolve_dn': 'uniquemember', + 'resolve_attribute': 'mail', + 'primary_domain': conf.get('kolab', 'primary_domain'), + 'fbsource': 'aggregate://%uniquemember', + 'directories': 'kolab-resources', + 'cacheto': '/var/cache/kolab-freebusy/%mail.ifb', + 'expires': '15m', + 'loglevel': 300, + }, } cfg_parser = RawConfigParser()
View file
pykolab-0.7.7.tar.gz/pykolab/setup/setup_ldap.py -> pykolab-0.7.8.tar.gz/pykolab/setup/setup_ldap.py
Changed
@@ -670,7 +670,10 @@ auth._auth.ldap.modify_s(dn, modlist) if os.path.isfile('/bin/systemctl'): - subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service']) + if not os.path.isfile('/usr/lib/systemd/system/dirsrv-admin.service'): + log.info(_("directory server admin service not available")) + else: + subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service']) elif os.path.isfile('/sbin/chkconfig'): subprocess.call(['/sbin/chkconfig', 'dirsrv-admin', 'on']) elif os.path.isfile('/usr/sbin/update-rc.d'):
View file
pykolab-0.7.7.tar.gz/pykolab/setup/setup_mysql.py -> pykolab-0.7.8.tar.gz/pykolab/setup/setup_mysql.py
Changed
@@ -39,8 +39,13 @@ return _("Setup MySQL.") def execute(*args, **kw): + # on CentOS7, there is MariaDB instead of MySQL + mysqlservice = 'mysqld.service' + if os.path.isfile('/usr/lib/systemd/system/mariadb.service'): + mysqlservice = 'mariadb.service' + if os.path.isfile('/bin/systemctl'): - subprocess.call(['/bin/systemctl', 'restart', 'mysqld.service']) + subprocess.call(['/bin/systemctl', 'restart', mysqlservice]) elif os.path.isfile('/sbin/service'): subprocess.call(['/sbin/service', 'mysqld', 'restart']) elif os.path.isfile('/usr/sbin/service'): @@ -49,7 +54,7 @@ log.error(_("Could not start the MySQL database service.")) if os.path.isfile('/bin/systemctl'): - subprocess.call(['/bin/systemctl', 'enable', 'mysqld.service']) + subprocess.call(['/bin/systemctl', 'enable', mysqlservice]) elif os.path.isfile('/sbin/chkconfig'): subprocess.call(['/sbin/chkconfig', 'mysqld', 'on']) elif os.path.isfile('/usr/sbin/update-rc.d'):
View file
pykolab-0.7.7.tar.gz/pykolab/setup/setup_roundcube.py -> pykolab-0.7.8.tar.gz/pykolab/setup/setup_roundcube.py
Changed
@@ -90,7 +90,7 @@ if os.access('/usr/share/roundcubemail/skins/enterprise/', os.R_OK): rc_settings['skin'] = 'enterprise' - elif os.access('/usr/share/roundcubemail/skin/chameleon/', os.R_OK): + elif os.access('/usr/share/roundcubemail/skins/chameleon/', os.R_OK): rc_settings['skin'] = 'chameleon' else: rc_settings['skin'] = 'larry' @@ -146,12 +146,13 @@ schema_files = [] for root, directories, filenames in os.walk('/usr/share/doc/'): + directories.sort() for directory in directories: if directory.startswith("roundcubemail"): - for root, directories, filenames in os.walk(os.path.join(root, directory)): - for filename in filenames: + for nested_root, nested_directories, nested_filenames in os.walk(os.path.join(root, directory)): + for filename in nested_filenames: if filename.startswith('mysql.initial') and filename.endswith('.sql'): - schema_filepath = os.path.join(root,filename) + schema_filepath = os.path.join(nested_root,filename) if not schema_filepath in schema_files: schema_files.append(schema_filepath)
View file
pykolab-0.7.7.tar.gz/pykolab/xml/__init__.py -> pykolab-0.7.8.tar.gz/pykolab/xml/__init__.py
Changed
@@ -9,6 +9,7 @@ from event import Event from event import EventIntegrityError from event import InvalidEventDateError +from event import InvalidEventStatusError from event import event_from_ical from event import event_from_string from event import event_from_message
View file
pykolab-0.7.7.tar.gz/pykolab/xml/event.py -> pykolab-0.7.8.tar.gz/pykolab/xml/event.py
Changed
@@ -35,8 +35,8 @@ return s -def event_from_ical(string): - return Event(from_ical=string) +def event_from_ical(ical, string=None): + return Event(from_ical=ical, from_string=string) def event_from_string(string): return Event(from_string=string) @@ -60,6 +60,7 @@ type = 'event' status_map = { + None: kolabformat.StatusUndefined, "TENTATIVE": kolabformat.StatusTentative, "CONFIRMED": kolabformat.StatusConfirmed, "CANCELLED": kolabformat.StatusCancelled, @@ -121,14 +122,14 @@ self._categories = [] self._attachment_parts = [] - if from_ical == "": + if isinstance(from_ical, str) and from_ical == "": if from_string == "": self.event = kolabformat.Event() else: self.event = kolabformat.readEvent(from_string, False) self._load_attendees() else: - self.from_ical(from_ical) + self.from_ical(from_ical, from_string) self.uid = self.get_uid() @@ -259,16 +260,27 @@ self.event.setAttendees(self._attendees) - def from_ical(self, ical): - if hasattr(icalendar.Event, 'from_ical'): + def from_ical(self, ical, raw=None): + if isinstance(ical, icalendar.Event) or isinstance(ical_event, icalendar.Calendar): + ical_event = ical + elif hasattr(icalendar.Event, 'from_ical'): ical_event = icalendar.Event.from_ical(ical) elif hasattr(icalendar.Event, 'from_string'): ical_event = icalendar.Event.from_string(ical) + # VCALENDAR block was given, find the first VEVENT item + if isinstance(ical_event, icalendar.Calendar): + for c in ical_event.walk(): + if c.name == 'VEVENT': + ical_event = c + break + # use the libkolab calendaring bindings to load the full iCal data if ical_event.has_key('RRULE') or ical_event.has_key('ATTACH') \ or [part for part in ical_event.walk() if part.name == 'VALARM']: - self._xml_from_ical(ical) + if raw is None or raw == "": + raw = ical if isinstance(ical, str) else ical.to_ical() + self._xml_from_ical(raw) else: self.event = kolabformat.Event() @@ -289,9 +301,11 @@ self.set_from_ical(attr.lower(), ical_event[attr]) def _xml_from_ical(self, ical): + if not "BEGIN:VCALENDAR" in ical: + ical = "BEGIN:VCALENDAR\nVERSION:2.0\n" + ical + "\nEND:VCALENDAR" from kolab.calendaring import EventCal self.event = EventCal() - self.event.fromICal("BEGIN:VCALENDAR\nVERSION:2.0\n" + ical + "\nEND:VCALENDAR") + self.event.fromICal(ical) def get_attendee_participant_status(self, attendee): return attendee.get_participant_status() @@ -851,7 +865,7 @@ self.event.setStatus(self.status_map[status]) elif status in self.status_map.values(): self.event.setStatus(status) - else: + elif not status == kolabformat.StatusUndefined: raise InvalidEventStatusError, _("Invalid status set: %r") % (status) def set_summary(self, summary):
View file
pykolab-0.7.7.tar.gz/pykolab/xml/recurrence_rule.py -> pykolab-0.7.8.tar.gz/pykolab/xml/recurrence_rule.py
Changed
@@ -1,3 +1,5 @@ +import icalendar +import datetime import kolabformat from pykolab.xml import utils as xmlutils @@ -91,18 +93,86 @@ else: kolabformat.RecurrenceRule.__init__(self, rrule) + def from_ical(self, vrecur): + vectorimap = { + 'BYSECOND': 'setBysecond', + 'BYMINUTE': 'setByminute', + 'BYHOUR': 'setByhour', + 'BYMONTHDAY': 'setBymonthday', + 'BYYEARDAY': 'setByyearday', + 'BYMONTH': 'setBymonth', + } + + settermap = { + 'FREQ': 'set_frequency', + 'INTERVAL': 'set_interval', + 'COUNT': 'set_count', + 'UNTIL': 'set_until', + 'WKST': 'set_weekstart', + 'BYDAY': 'set_byday', + } + + for prop,setter in vectorimap.items(): + if vrecur.has_key(prop): + getattr(self, setter)([int(v) for v in vrecur[prop]]) + + for prop,setter in settermap.items(): + if vrecur.has_key(prop): + getattr(self, setter)(vrecur[prop]) + + def set_count(self, count): + if isinstance(count, list): + count = count[0] + self.setCount(int(count)) + + def set_interval(self, val): + if isinstance(val, list): + val = val[0] + self.setInterval(int(val)) + + def set_frequency(self, freq): + self._set_map_value(freq, self.frequency_map, 'setFrequency') + def get_frequency(self, translated=False): freq = self.frequency() if translated: return self._translate_value(freq, self.frequency_map) return freq + def set_byday(self, bdays): + daypos = kolabformat.vectordaypos() + for wday in bdays: + weekday = str(wday)[-2:] + occurrence = int(wday.relative) + if str(wday)[0] == '-': + occurrence = occurrence * -1 + if self.weekday_map.has_key(weekday): + daypos.append(kolabformat.DayPos(occurrence, self.weekday_map[weekday])) + self.setByday(daypos) + + def set_weekstart(self, wkst): + self._set_map_value(wkst, self.weekday_map, 'setWeekStart') + def get_weekstart(self, translated=False): wkst = self.weekStart() if translated: return self._translate_value(wkst, self.weekday_map) return wkst + def set_until(self, until): + if isinstance(until, list): + until = until[0] + if isinstance(until, datetime.datetime) or isinstance(until, datetime.date): + self.setEnd(xmlutils.to_cdatetime(until, True)) + + def _set_map_value(self, val, pmap, setter): + if isinstance(val, list): + val = val[0] + if val in pmap.keys(): + getattr(self, setter)(pmap[val]) + elif val in pmap.values(): + getattr(self, setter)(val) + def _translate_value(self, val, map): name_map = dict([(v, k) for (k, v) in map.iteritems()]) return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
View file
pykolab-0.7.7.tar.gz/pykolab/xml/todo.py -> pykolab-0.7.8.tar.gz/pykolab/xml/todo.py
Changed
@@ -2,18 +2,20 @@ import kolabformat import icalendar import pytz +import base64 import pykolab from pykolab import constants from pykolab.xml import Event +from pykolab.xml import RecurrenceRule from pykolab.xml import utils as xmlutils from pykolab.xml.event import InvalidEventDateError from pykolab.translate import _ log = pykolab.getLogger('pykolab.xml_todo') -def todo_from_ical(string): - return Todo(from_ical=string) +def todo_from_ical(ical, string=None): + return Todo(from_ical=ical, from_string=string) def todo_from_string(string): return Todo(from_string=string) @@ -48,26 +50,40 @@ "end": "void" }) - if from_ical == "": + if isinstance(from_ical, str) and from_ical == "": if from_string == "": self.event = kolabformat.Todo() else: self.event = kolabformat.readTodo(from_string, False) self._load_attendees() else: - self.from_ical(from_ical) + self.from_ical(from_ical, from_string) self.uid = self.get_uid() - def from_ical(self, ical): - if hasattr(icalendar.Todo, 'from_ical'): + def from_ical(self, ical, raw): + if isinstance(ical, icalendar.Todo): + ical_todo = ical + elif hasattr(icalendar.Todo, 'from_ical'): ical_todo = icalendar.Todo.from_ical(ical) elif hasattr(icalendar.Todo, 'from_string'): ical_todo = icalendar.Todo.from_string(ical) - # use the libkolab calendaring bindings to load the full iCal data - if ical_todo.has_key('ATTACH') or [part for part in ical_todo.walk() if part.name == 'VALARM']: - self._xml_from_ical(ical) + # VCALENDAR block was given, find the first VTODO item + if isinstance(ical_todo, icalendar.Calendar): + for c in ical_todo.walk(): + if c.name == 'VTODO': + ical_todo = c + break + + log.debug("Todo.from_ical(); %r, %r, %r" % (type(ical_todo), ical_todo.has_key('ATTACH'), ical_todo.has_key('ATTENDEE')), level=8) + + # DISABLED: use the libkolab calendaring bindings to load the full iCal data + # TODO: this requires support for iCal parsing in the kolab.calendaring bindings + if False and ical_todo.has_key('ATTACH') or [part for part in ical_todo.walk() if part.name == 'VALARM']: + if raw is None or raw == "": + raw = ical if isinstance(ical, str) else ical.to_ical() + self._xml_from_ical(raw) else: self.event = kolabformat.Todo() @@ -88,8 +104,39 @@ self.set_from_ical('percentcomplete', ical_todo['PERCENT-COMPLETE']) def _xml_from_ical(self, ical): + # FIXME: kolabformat or kolab.calendaring modules do not provide bindings to import Todo from iCal self.event = Todo() - self.event.fromICal("BEGIN:VCALENDAR\nVERSION:2.0\n" + ical + "\nEND:VCALENDAR") + + def set_ical_attach(self, attachment): + if hasattr(attachment, 'params'): + params = attachment.params + else: + params = {} + + _attachment = kolabformat.Attachment() + if params.has_key('FMTTYPE'): + mimetype = str(params['FMTTYPE']) + else: + mimetype = 'application/octet-stream' + + if params.has_key('X-LABEL'): + _attachment.setLabel(str(params['X-LABEL'])) + + decode = False + if params.has_key('ENCODING'): + if params['ENCODING'] == "BASE64" or params['ENCODING'] == "B": + decode = True + + _attachment.setData(base64.b64decode(str(attachment)) if decode else str(attachment), mimetype) + vattach = self.event.attachments() + vattach.append(_attachment) + self.event.setAttachments(vattach) + + def set_ical_rrule(self, rrule): + _rrule = RecurrenceRule() + _rrule.from_ical(rrule) + if _rrule.isValid(): + self.event.setRecurrenceRule(_rrule) def set_ical_due(self, due): self.set_due(due)
View file
pykolab-0.7.7.tar.gz/tests/functional/test_wallace/test_002_footer.py -> pykolab-0.7.8.tar.gz/tests/functional/test_wallace/test_002_footer.py
Changed
@@ -18,6 +18,14 @@ conf = pykolab.getConf() class TestWallaceFooter(unittest.TestCase): + user = None + + @classmethod + def setUp(self): + """ Compatibility for twisted.trial.unittest + """ + if not self.user: + self.setup_class() @classmethod def setup_class(self, *args, **kw):
View file
pykolab-0.7.7.tar.gz/tests/functional/test_wallace/test_007_invitationpolicy.py -> pykolab-0.7.8.tar.gz/tests/functional/test_wallace/test_007_invitationpolicy.py
Changed
@@ -14,6 +14,7 @@ from pykolab.xml import event_from_message from pykolab.xml import todo_from_message from pykolab.xml import participant_status_label +from pykolab.itip import events_from_message from email import message_from_string from twisted.trial import unittest @@ -287,6 +288,28 @@ 'kolabinvitationpolicy': ['ACT_ACCEPT','ACT_UPDATE_AND_NOTIFY'] } + self.lucy = { + 'displayname': 'Lucy Meyer', + 'mail': 'lucy.meyer@example.org', + 'dn': 'uid=meyer,ou=People,dc=example,dc=org', + 'preferredlanguage': 'en_US', + 'mailbox': 'user/lucy.meyer@example.org', + 'kolabcalendarfolder': 'user/lucy.meyer/Calendar@example.org', + 'kolabtasksfolder': 'user/lucy.meyer/Tasks@example.org', + 'kolabinvitationpolicy': ['ALL_SAVE_AND_FORWARD','ACT_UPDATE_AND_NOTIFY'] + } + + self.bill = { + 'displayname': 'Bill Mayor', + 'mail': 'bill.mayor@example.org', + 'dn': 'uid=mayor,ou=People,dc=example,dc=org', + 'preferredlanguage': 'en_US', + 'mailbox': 'user/bill.mayor@example.org', + 'kolabcalendarfolder': 'user/bill.mayor/Calendar@example.org', + 'kolabtasksfolder': 'user/bill.mayor/Tasks@example.org', + 'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:lucy.meyer@example.org','ALL_REJECT'] + } + self.external = { 'displayname': 'Bob External', 'mail': 'bob.external@gmail.com' @@ -294,13 +317,16 @@ from tests.functional.user_add import user_add user_add("John", "Doe", kolabinvitationpolicy=self.john['kolabinvitationpolicy'], preferredlanguage=self.john['preferredlanguage']) - user_add("Jane", "Manager", kolabinvitationpolicy=self.jane['kolabinvitationpolicy'], preferredlanguage=self.jane['preferredlanguage']) + user_add("Jane", "Manager", kolabinvitationpolicy=self.jane['kolabinvitationpolicy'], preferredlanguage=self.jane['preferredlanguage'], kolabdelegate=[self.mark['dn']]) user_add("Jack", "Tentative", kolabinvitationpolicy=self.jack['kolabinvitationpolicy'], preferredlanguage=self.jack['preferredlanguage']) user_add("Mark", "German", kolabinvitationpolicy=self.mark['kolabinvitationpolicy'], preferredlanguage=self.mark['preferredlanguage']) + user_add("Lucy", "Meyer", kolabinvitationpolicy=self.lucy['kolabinvitationpolicy'], preferredlanguage=self.lucy['preferredlanguage']) + user_add("Bill", "Mayor", kolabinvitationpolicy=self.bill['kolabinvitationpolicy'], preferredlanguage=self.bill['preferredlanguage']) time.sleep(1) from tests.functional.synchronize import synchronize_once synchronize_once() + time.sleep(4) # create confidential calendar folder for jane imap = IMAP() @@ -313,6 +339,8 @@ } } }) + # grant full access for Mark to Jane's calendar + imap.set_acl(imap.folder_quote(self.jane['kolabcalendarfolder']), self.mark['mail'], "lrswipkxtecda") imap.disconnect() @@ -323,7 +351,7 @@ 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'): + def send_itip_invitation(self, attendee_email, start=None, allday=False, template=None, summary="test", sequence=0, partstat='NEEDS-ACTION', from_addr=None): if start is None: start = datetime.datetime.now() @@ -338,6 +366,12 @@ default_template = itip_invitation date_format = '%Y%m%dT%H%M%S' + if from_addr is not None: + if template: + template = template.replace("john.doe@example.org", from_addr) + else: + default_template = default_template.replace("john.doe@example.org", from_addr) + self.send_message((template if template is not None else default_template) % { 'uid': uid, 'start': start.strftime(date_format), @@ -347,7 +381,7 @@ 'sequence': sequence, 'partstat': partstat }, - attendee_email) + attendee_email, from_addr=from_addr) return uid @@ -656,6 +690,26 @@ self.assertEqual(event.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartNeedsAction) + def test_004_copy_to_calendar_and_forward(self): + uid = self.send_itip_invitation(self.lucy['mail'], datetime.datetime(2015,2,11, 14,0,0), summary="test forward") + response = self.check_message_received(self.itip_reply_subject % { 'summary':'test forward', 'status':participant_status_label('ACCEPTED') }, self.lucy['mail'], self.lucy['mailbox']) + self.assertEqual(response, None, "No reply expected") + + event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + self.assertEqual(event.get_summary(), "test forward") + self.assertEqual(event.get_attendee(self.lucy['mail']).get_participant_status(), kolabformat.PartNeedsAction) + + # find original itip invitation in user's inbox + message = self.check_message_received('"test"', 'john.doe@example.org', self.lucy['mailbox']) + self.assertIsInstance(message, email.message.Message) + + itips = events_from_message(message) + self.assertEqual(len(itips), 1); + self.assertEqual(itips[0]['method'], 'REQUEST'); + self.assertEqual(itips[0]['uid'], uid); + + def test_005_invite_rescheduling_accept(self): self.purge_mailbox(self.john['mailbox']) @@ -958,6 +1012,40 @@ self.assertEqual(event.get_summary(), "confidential") + def test_013_update_shared_folder(self): + # create an event organized by Mark (a delegate of Jane) into Jane's calendar + start = datetime.datetime(2015,3,10, 9,30,0, tzinfo=pytz.timezone("Europe/Berlin")) + uid = self.create_calendar_event(start, user=self.mark, attendees=[self.jane, self.john], folder=self.jane['kolabcalendarfolder']) + + event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + + # send a reply from john to mark + self.send_itip_reply(uid, self.john['mail'], self.mark['mail'], start=start) + + # check for the updated event in jane's calendar + time.sleep(10) + event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + self.assertEqual(event.get_attendee(self.john['mail']).get_participant_status(), kolabformat.PartAccepted) + + def test_014_per_sender_policy(self): + # send invitation from john => REJECT + start = datetime.datetime(2015,2,28, 14,0,0) + uid = self.send_itip_invitation(self.bill['mail'], start) + + response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.bill['mail']) + self.assertIsInstance(response, email.message.Message) + + # send invitation from lucy => SAVE + start = datetime.datetime(2015,3,11, 10,0,0) + templ = itip_invitation.replace("Doe, John", self.lucy['displayname']) + uid = self.send_itip_invitation(self.bill['mail'], start, template=templ, from_addr=self.lucy['mail']) + + event = self.check_user_calendar_event(self.bill['kolabcalendarfolder'], uid) + self.assertIsInstance(event, pykolab.xml.Event) + + def test_020_task_assignment_accept(self): start = datetime.datetime(2014,9,10, 19,0,0) uid = self.send_itip_invitation(self.jane['mail'], start, summary='work', template=itip_todo)
View file
pykolab-0.7.7.tar.gz/tests/unit/test-003-event.py -> pykolab-0.7.8.tar.gz/tests/unit/test-003-event.py
Changed
@@ -369,7 +369,7 @@ """ + ical_event + "END:VCALENDAR" ical = icalendar.Calendar.from_ical(ical_str) - event = event_from_ical(ical.walk('VEVENT')[0].to_ical()) + event = event_from_ical(ical.walk('VEVENT')[0], ical_str) self.assertEqual(event.get_location(), "Location") self.assertEqual(str(event.get_lastmodified()), "2014-04-07 12:23:11+00:00") @@ -384,7 +384,7 @@ self.assertTrue(event.is_recurring()) self.assertIsInstance(event.get_duration(), datetime.timedelta) self.assertIsInstance(event.get_end(), datetime.datetime) - self.assertEqual(str(event.get_end()), "2014-05-23 12:30:00+01:00") + self.assertEqual(str(event.get_end()), "2014-05-23 12:30:00+02:00") self.assertEqual(len(event.get_exception_dates()), 2) self.assertIsInstance(event.get_exception_dates()[0], datetime.datetime) self.assertEqual(len(event.get_alarms()), 1) @@ -632,7 +632,7 @@ self.assertEqual(data['attach'][0]['fmttype'], 'text/html') self.assertIsInstance(data['rrule'], dict) - self.assertEqual(data['rrule']['frequency'], 'DAILY') + self.assertEqual(data['rrule']['freq'], 'DAILY') self.assertEqual(data['rrule']['interval'], 1) self.assertEqual(data['rrule']['wkst'], 'MO') self.assertIsInstance(data['rrule']['until'], datetime.date)
View file
pykolab-0.7.7.tar.gz/tests/unit/test-012-wallace_invitationpolicy.py -> pykolab-0.7.8.tar.gz/tests/unit/test-012-wallace_invitationpolicy.py
Changed
@@ -122,6 +122,7 @@ 'TASK_REJECT:*', 'EVENT_ACCEPT:example.org', 'EVENT_REJECT:gmail.com', + 'ALL_UPDATE:outlook:com', 'ALL_MANUAL:*' ] } self.assertEqual(MIP.get_matching_invitation_policies(user, 'a@fastmail.net', MIP.COND_TYPE_EVENT), [MIP.ACT_MANUAL]) @@ -129,8 +130,9 @@ self.assertEqual(MIP.get_matching_invitation_policies(user, 'c@gmail.com', MIP.COND_TYPE_EVENT), [MIP.ACT_REJECT, MIP.ACT_MANUAL]) self.assertEqual(MIP.get_matching_invitation_policies(user, 'd@somedomain.net', MIP.COND_TYPE_TASK), [MIP.ACT_REJECT, MIP.ACT_MANUAL]) - user = { 'kolabinvitationpolicy': ['ACT_ACCEPT:example.org', 'ACT_MANUAL:others'] } - self.assertEqual(MIP.get_matching_invitation_policies(user, 'd@somedomain.net', MIP.COND_TYPE_ALL), [MIP.ACT_MANUAL]) + user = { 'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:maya.foo@example.org', 'ACT_REJECT:others'] } + self.assertEqual(MIP.get_matching_invitation_policies(user, 'maya.foo@example.org', MIP.COND_TYPE_ALL), [MIP.ACT_SAVE_TO_FOLDER]) + self.assertEqual(MIP.get_matching_invitation_policies(user, 'd@somedomain.net', MIP.COND_TYPE_ALL), [MIP.ACT_MANUAL]) def test_004_write_locks(self): user = { 'cn': 'John Doe', 'mail': "doe@example.org" }
View file
pykolab-0.7.7.tar.gz/tests/unit/test-016-todo.py -> pykolab-0.7.8.tar.gz/tests/unit/test-016-todo.py
Changed
@@ -8,6 +8,7 @@ from pykolab.xml import Attendee from pykolab.xml import Todo from pykolab.xml import TodoIntegrityError +from pykolab.xml import InvalidEventStatusError from pykolab.xml import todo_from_ical from pykolab.xml import todo_from_string from pykolab.xml import todo_from_message @@ -25,6 +26,7 @@ LAST-MODIFIED;VALUE=DATE-TIME:20140820T101333Z DTSTART;VALUE=DATE-TIME;TZID=Europe/London:20140818T180000 DUE;VALUE=DATE-TIME;TZID=Europe/London:20140822T133000 +RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=2MO,-1WE;UNTIL=20150220T180000Z SUMMARY:Sample Task assignment DESCRIPTION:Summary: Sample Task assignment\\nDue Date: 08/11/14\\nDue Time: \\n13:30 AM @@ -40,6 +42,43 @@ END:VCALENDAR """ +ical_todo_attachment = """ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject + 2.1.3//EN +CALSCALE:GREGORIAN +BEGIN:VTODO +UID:18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0 +DTSTAMP;VALUE=DATE-TIME:20140820T101333Z +CREATED;VALUE=DATE-TIME:20140731T100704Z +LAST-MODIFIED;VALUE=DATE-TIME:20140820T101333Z +DUE;VALUE=DATE-TIME;TZID=Europe/London:20150228T133000 +SUMMARY:Task with attachment +SEQUENCE:3 +PRIORITY:1 +STATUS:IN-PROCESS +ORGANIZER;CN=Thomas:mailto:thomas.bruederli@example.org +ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=image/png;X-LABEL=silhouette.pn + g:iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAIAAADY27xgAAAAGXRFWHRTb2Z0d2FyZQBBZG9i + ZSBJbWFnZVJlYWR5ccllPAAAAsRJREFUeNrsmeluKjEMhTswrAWB4P3fECGx79CjsTDmOKRkpF + xxpfoHSmchX7ybFrfb7eszpPH1MfKH8ofyH6KUtd/c7/en0wmfWBdF0Wq1Op1Ou91uNGoer6iX + V1ar1Xa7xUJeB4qsr9frdyVlWWZH2VZyPp+xPXHIAoK70+m02+1m9JXj8bhcLi+Xi3J4xUCazS + bUltdtd7ud7ldUIhC3u+iTwF0sFhlR4Kds4LtRZK1w4te5UM6V6JaqhqC3CQ28OAsKggJfbZ3U + eozCqZ4koHIZCGmD9ivuos9YONFirmxrI0UNZG1kbZeUXdJQNJNa91RlqMn0ekYUMZDup6dXVV + m+1OSZhqLx6bVCELJGSsyFQtFrF15JGYMZgoxubWGDSDVhvTipDKWhoBOIpFobxtlbJ0Gh0/tg + lgXal4woUHi/36fQoBQncDAlupa8DeVwOPRe4lUyGAwQ+dl7W+xBXkJBhEUqR32UoJfYIKrR4d + ZBgcdIRqfEqn+mekl9FNRbSTA249la3ev1/kXHD47ZbEYR5L9kMplkd9vNZqMFyIYxxfN8Pk8q + QGlagT5QDtfrNYUMlWW9LiGNPPSmC/+OgpK2r4RO6dOatZd+4gAAemdIi6Fg9EKLD4vASWkzv3 + ew06NSCiA40CumAIoaIrhrcAwjF7aDo58gUchgNV+0n1BAcDgcoAZrXV9mI4qkhtK6FJFhi9Fo + ZKPsgQI1ACJieH/Kd570t+xFoIzHYzl5Q40CFGrSqGuks3qmYIKJfIl0nPKLxAMFw7Dv1+2QYf + vFSOBQubbOFDSc7ZcfWvHv6DzhOzT6IeOVPuz8Roex0f6EgsE/2IL4qdg7hIXz7/pBie7q1uWr + tp66xrif0l1KwUE4P7Y9Gci/ZgtNRFX+Rw06Q2RigsjuDc3urwKHxuNITaaxyD9mT2WvSDAXn/ + Pvhh8BBgBjyfPSGbSYcwAAAABJRU5ErkJggg== +END:VTODO +END:VCALENDAR +""" + xml_todo = """ <icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> <vcalendar> @@ -164,15 +203,20 @@ self.assertIsInstance(self.todo.__str__(), str) def test_002_full(self): - pass + self.todo.set_summary("test full") + status = self.todo.get_status() + self.assertEqual(status, kolabformat.StatusUndefined) + self.assertRaises(InvalidEventStatusError, self.todo.set_status, (-1)) + self.todo.set_status(status) + # TODO: add more setters and getter tests here def test_010_load_from_xml(self): todo = todo_from_string(xml_todo) self.assertEqual(todo.uid, '18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0') self.assertEqual(todo.get_sequence(), 3) self.assertIsInstance(todo.get_due(), datetime.datetime) - self.assertEqual(str(todo.get_due()), "2014-08-22 13:30:00+01:00") - self.assertEqual(str(todo.get_start()), "2014-08-18 18:00:00+01:00") + self.assertEqual(str(todo.get_due()), "2014-08-22 13:30:00+02:00") + self.assertEqual(str(todo.get_start()), "2014-08-18 18:00:00+02:00") self.assertEqual(todo.get_categories(), ['iTip']) self.assertEqual(todo.get_attendee_by_email("john@example.org").get_participant_status(), kolabformat.PartNeedsAction) self.assertIsInstance(todo.get_organizer(), kolabformat.ContactReference) @@ -198,6 +242,13 @@ self.assertEqual(todo.get_percentcomplete(), 20) #print str(todo) + data = todo.to_dict() + self.assertIsInstance(data['rrule'], dict) + self.assertEqual(data['rrule']['freq'], 'MONTHLY') + self.assertEqual(data['rrule']['interval'], 2) + self.assertEqual(data['rrule']['byday'], '2MO,-1WE') + self.assertIsInstance(data['rrule']['until'], datetime.datetime) + def test_021_as_string_itip(self): self.todo.set_summary("test") self.todo.set_start(datetime.datetime(2014, 9, 20, 11, 00, 00, tzinfo=pytz.timezone("Europe/London"))) @@ -217,6 +268,16 @@ self.assertIsInstance(vtodo['dtstamp'].dt, datetime.datetime) + def test_022_ical_with_attachment(self): + todo = todo_from_ical(ical_todo_attachment) + + vattachment = todo.get_attachments() + self.assertEqual(len(vattachment), 1) + + attachment = vattachment[0] + self.assertEqual(attachment.mimetype(), 'image/png') + self.assertEqual(attachment.label(), 'silhouette.png') + def test_030_to_dict(self): data = todo_from_string(xml_todo).to_dict()
View file
pykolab-0.7.7.tar.gz/wallace/module_footer.py -> pykolab-0.7.8.tar.gz/wallace/module_footer.py
Changed
@@ -21,12 +21,7 @@ import tempfile import time -from email import message_from_string -from email.MIMEBase import MIMEBase -from email.MIMEText import MIMEText -from email.parser import Parser -from email.utils import formataddr -from email.utils import getaddresses +from email import message_from_file import modules @@ -70,9 +65,8 @@ os.rename(filepath, new_filepath) filepath = new_filepath - # parse message headers - # @TODO: make sure we can use True as the 2nd argument here - message = Parser().parse(open(filepath, 'r'), True) + # parse message + message = message_from_file(open(filepath, 'r')) # Possible footer answers are limited to ACCEPT only answers = [ 'ACCEPT' ] @@ -83,6 +77,7 @@ footer_text_file = conf.get('wallace', 'footer_text') if not os.path.isfile(footer_text_file) and not os.path.isfile(footer_html_file): + log.warning(_("No contents configured for footer module")) exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', filepath)) return @@ -111,53 +106,41 @@ exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', filepath)) return - if message.is_multipart(): - if message.get_content_type() == "multipart/alternative": - log.debug("The message content type is multipart/alternative.") + for part in message.walk(): + disposition = None - for part in message.walk(): - disposition = None + try: + content_type = part.get_content_type() + except: + continue - try: - content_type = part.get_content_type() - except: - continue + try: + disposition = part.get("Content-Disposition") + except: + pass - try: - disposition = part.get("Content-Disposition") - except: - pass + log.debug("Walking message part: %s; disposition = %r" % (content_type, disposition), level=8) - if not disposition == None: - continue + if not disposition == None: + continue - if content_type == "text/plain": - content = part.get_payload() - content += "\n\n--\n%s" % (footer['plain']) - part.set_payload(content) - footer_added = True - - elif content_type == "text/html": - content = part.get_payload() - content += "\n<!-- footer appended by Wallace -->\n" - content += "\n<html><body><hr />%s</body></html>\n" % (footer['html']) - part.set_payload(content) - footer_added = True - - else: - # Check the main content-type. - if message.get_content_type() == "text/html": - content = message.get_payload() - content += "\n<!-- footer appended by Wallace -->\n" - content += "\n<html><body><hr />%s</body></html>\n" % (footer['html']) - message.set_payload(content) + if content_type == "text/plain": + content = part.get_payload() + content += "\n\n-- \n%s" % (footer['plain']) + part.set_payload(content) footer_added = True + log.debug("Text footer attached.", level=6) + + elif content_type == "text/html": + content = part.get_payload() + append = "\n<!-- footer appended by Wallace -->\n" + footer['html'] + if "</body>" in content: + part.set_payload(content.replace("</body>", append + "</body>")) + else: + part.set_payload("<html><body>" + content + append + "</body></html>") - else: - content = message.get_payload() - content += "\n\n--\n%s" % (footer['plain']) - message.set_payload(content) footer_added = True + log.debug("HTML footer attached.", level=6) if footer_added: log.debug("Footer attached.")
View file
pykolab-0.7.7.tar.gz/wallace/module_invitationpolicy.py -> pykolab-0.7.8.tar.gz/wallace/module_invitationpolicy.py
Changed
@@ -62,12 +62,14 @@ COND_IF_CONFLICT = 128 COND_TENTATIVE = 256 COND_NOTIFY = 512 +COND_FORWARD = 4096 COND_TYPE_EVENT = 1024 COND_TYPE_TASK = 2048 COND_TYPE_ALL = COND_TYPE_EVENT + COND_TYPE_TASK ACT_TENTATIVE = ACT_ACCEPT + COND_TENTATIVE ACT_UPDATE_AND_NOTIFY = ACT_UPDATE + COND_NOTIFY +ACT_SAVE_AND_FORWARD = ACT_SAVE_TO_FOLDER + COND_FORWARD FOLDER_TYPE_ANNOTATION = '/vendor/kolab/folder-type' @@ -83,6 +85,7 @@ 'ALL_UPDATE': ACT_UPDATE + COND_TYPE_ALL, 'ALL_UPDATE_AND_NOTIFY': ACT_UPDATE_AND_NOTIFY + COND_TYPE_ALL, 'ALL_SAVE_TO_FOLDER': ACT_SAVE_TO_FOLDER + COND_TYPE_ALL, + 'ALL_SAVE_AND_FORWARD': ACT_SAVE_AND_FORWARD + COND_TYPE_ALL, # event related policy values 'EVENT_MANUAL': ACT_MANUAL + COND_TYPE_EVENT, 'EVENT_ACCEPT': ACT_ACCEPT + COND_TYPE_EVENT, @@ -96,6 +99,7 @@ 'EVENT_DELEGATE_IF_CONFLICT': ACT_DELEGATE + COND_IF_CONFLICT + COND_TYPE_EVENT, 'EVENT_REJECT_IF_CONFLICT': ACT_REJECT + COND_IF_CONFLICT + COND_TYPE_EVENT, 'EVENT_SAVE_TO_FOLDER': ACT_SAVE_TO_FOLDER + COND_TYPE_EVENT, + 'EVENT_SAVE_AND_FORWARD': ACT_SAVE_AND_FORWARD + COND_TYPE_EVENT, # task related policy values 'TASK_MANUAL': ACT_MANUAL + COND_TYPE_TASK, 'TASK_ACCEPT': ACT_ACCEPT + COND_TYPE_TASK, @@ -104,6 +108,7 @@ 'TASK_UPDATE': ACT_UPDATE + COND_TYPE_TASK, 'TASK_UPDATE_AND_NOTIFY': ACT_UPDATE_AND_NOTIFY + COND_TYPE_TASK, 'TASK_SAVE_TO_FOLDER': ACT_SAVE_TO_FOLDER + COND_TYPE_TASK, + 'TASK_SAVE_AND_FORWARD': ACT_SAVE_AND_FORWARD + COND_TYPE_TASK, # legacy values 'ACT_MANUAL': ACT_MANUAL + COND_TYPE_ALL, 'ACT_ACCEPT': ACT_ACCEPT + COND_TYPE_ALL, @@ -117,6 +122,7 @@ 'ACT_UPDATE': ACT_UPDATE + COND_TYPE_ALL, 'ACT_UPDATE_AND_NOTIFY': ACT_UPDATE_AND_NOTIFY + COND_TYPE_ALL, 'ACT_SAVE_TO_CALENDAR': ACT_SAVE_TO_FOLDER + COND_TYPE_EVENT, + 'ACT_SAVE_AND_FORWARD': ACT_SAVE_AND_FORWARD + COND_TYPE_EVENT, } policy_value_map = dict([(v &~ COND_TYPE_ALL, k) for (k, v) in policy_name_map.iteritems()]) @@ -275,6 +281,12 @@ receiving_user = auth.get_entry_attributes(None, recipient_user_dn, ['*']) recipient_emails = auth.extract_recipient_addresses(receiving_user) recipient_email = recipient + + # extend with addresses from delegators + receiving_user['_delegated_mailboxes'] = [] + for _delegator in auth.list_delegators(recipient_user_dn): + receiving_user['_delegated_mailboxes'].append(_delegator['_mailbox_basename'].split('@')[0]) + log.debug(_("Recipient emails for %s: %r") % (recipient_user_dn, recipient_emails), level=8) break @@ -467,7 +479,11 @@ if not nonpart or existing: # save new copy from iTip if store_object(itip_event['xml'], receiving_user, targetfolder): - return MESSAGE_PROCESSED + if policy & COND_FORWARD: + log.debug(_("Forward invitation for notification"), level=5) + return MESSAGE_FORWARD + else: + return MESSAGE_PROCESSED return None @@ -645,7 +661,7 @@ matches = [] for p in policies: if ':' in p: - (value, domain) = p.split(':') + (value, domain) = p.split(':', 1) else: value = p domain = '' @@ -717,15 +733,14 @@ (ns_personal, ns_other, ns_shared) = imap.namespaces() - if isinstance(ns_shared, list): - ns_shared = ns_shared[0] - if isinstance(ns_other, list): - ns_other = ns_other[0] - for folder in folders: # exclude shared and other user's namespace + if not ns_other is None and folder.startswith(ns_other): + # allow shared folders from delegators + if len([_mailbox for _mailbox in user_rec['_delegated_mailboxes'] if folder.startswith(ns_other + _mailbox + '/')]) == 0: + continue; # TODO: list shared folders the user has write privileges ? - if folder.startswith(ns_other) or folder.startswith(ns_shared): + if not ns_shared is None and len([_ns for _ns in ns_shared if folder.startswith(_ns)]) > 0: continue; metadata = imap.get_metadata(folder)
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.7-0~kolab2 +Version: 0.7.8-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.7.tar.gz + 00000000000000000000000000000000 0 pykolab-0.7.8.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
.