Changeset View
Changeset View
Standalone View
Standalone View
wallace/module_resources.py
Show First 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | def accept(filepath): | ||||
exec('modules.cb_action_ACCEPT(%r, %r)' % ('resources',filepath)) | exec('modules.cb_action_ACCEPT(%r, %r)' % ('resources',filepath)) | ||||
def description(): | def description(): | ||||
return """Resource management module.""" | return """Resource management module.""" | ||||
def cleanup(): | def cleanup(): | ||||
global auth, imap | global auth, imap | ||||
log.debug("cleanup(): %r, %r" % (auth, imap), level=9) | log.debug("cleanup(): %r, %r" % (auth, imap), level=8) | ||||
auth.disconnect() | auth.disconnect() | ||||
del auth | del auth | ||||
# Disconnect IMAP or we lock the mailbox almost constantly | # Disconnect IMAP or we lock the mailbox almost constantly | ||||
imap.disconnect() | imap.disconnect() | ||||
del imap | del imap | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | if not len(itip_events) > 0: | ||||
) | ) | ||||
else: | else: | ||||
any_itips = True | any_itips = True | ||||
log.debug( | log.debug( | ||||
_("iTip events attached to this message contain the " + \ | _("iTip events attached to this message contain the " + \ | ||||
"following information: %r") % (itip_events), | "following information: %r") % (itip_events), | ||||
level=9 | level=8 | ||||
) | ) | ||||
if any_itips: | if any_itips: | ||||
# See if any iTip actually allocates a resource. | # See if any iTip actually allocates a resource. | ||||
if len([x['resources'] for x in itip_events if x.has_key('resources')]) > 0 \ | if len([x['resources'] for x in itip_events if x.has_key('resources')]) > 0 \ | ||||
or len([x['attendees'] for x in itip_events if x.has_key('attendees')]) > 0: | or len([x['attendees'] for x in itip_events if x.has_key('attendees')]) > 0: | ||||
possibly_any_resources = True | possibly_any_resources = True | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | for itip_event in itip_events: | ||||
if reference_uid: | if reference_uid: | ||||
(event, master) = find_existing_event(reference_uid, itip_event['recurrence-id'], 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) | log.debug(_("iTip REPLY to %r, %r; matches %r") % (reference_uid, itip_event['recurrence-id'], type(event)), level=8) | ||||
if event: | if event: | ||||
try: | try: | ||||
sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | ||||
owner_reply = sender_attendee.get_participant_status() | owner_reply = sender_attendee.get_participant_status() | ||||
log.debug(_("Sender Attendee: %r => %r") % (sender_attendee, owner_reply), level=9) | log.debug(_("Sender Attendee: %r => %r") % (sender_attendee, owner_reply), level=8) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Could not find envelope sender attendee: %r") % (e)) | log.error(_("Could not find envelope sender attendee: %r") % (e)) | ||||
continue | continue | ||||
# compare sequence number to avoid outdated replies | # compare sequence number to avoid outdated replies | ||||
if not itip_event['sequence'] == event.get_sequence(): | if not itip_event['sequence'] == event.get_sequence(): | ||||
log.info(_("The iTip reply sequence (%r) doesn't match the referred event version (%r). Ignoring.") % ( | log.info(_("The iTip reply sequence (%r) doesn't match the referred event version (%r). Ignoring.") % ( | ||||
itip_event['sequence'], event.get_sequence() | itip_event['sequence'], event.get_sequence() | ||||
Show All 25 Lines | for itip_event in itip_events: | ||||
# exit for-loop | # exit for-loop | ||||
break | break | ||||
# else: | # else: | ||||
try: | try: | ||||
receiving_attendee = itip_event['xml'].get_attendee_by_email(receiving_resource['mail']) | receiving_attendee = itip_event['xml'].get_attendee_by_email(receiving_resource['mail']) | ||||
log.debug(_("Receiving Resource: %r; %r") % (receiving_resource, receiving_attendee), level=9) | log.debug(_("Receiving Resource: %r; %r") % (receiving_resource, receiving_attendee), level=8) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Could not find envelope attendee: %r") % (e)) | log.error(_("Could not find envelope attendee: %r") % (e)) | ||||
continue | continue | ||||
# ignore updates and cancellations to resource collections who already delegated the event | # ignore updates and cancellations to resource collections who already delegated the event | ||||
if len(receiving_attendee.get_delegated_to()) > 0 or receiving_attendee.get_role() == kolabformat.NonParticipant: | if len(receiving_attendee.get_delegated_to()) > 0 or receiving_attendee.get_role() == kolabformat.NonParticipant: | ||||
done = True | done = True | ||||
log.debug(_("Recipient %r is non-participant, ignoring message") % (receiving_resource['mail']), level=8) | log.debug(_("Recipient %r is non-participant, ignoring message") % (receiving_resource['mail']), level=8) | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if available_resource is not None: | ||||
delegator.set_rsvp(False) | delegator.set_rsvp(False) | ||||
log.debug(_("Delegate invitation for resource collection %r to %r") % (original_resource['mail'], available_resource['mail']), level=8) | log.debug(_("Delegate invitation for resource collection %r to %r") % (original_resource['mail'], available_resource['mail']), level=8) | ||||
resource = available_resource | resource = available_resource | ||||
# Look for ACT_REJECT policy | # Look for ACT_REJECT policy | ||||
if resource is not None: | if resource is not None: | ||||
invitationpolicy = get_resource_invitationpolicy(resource) | invitationpolicy = get_resource_invitationpolicy(resource) | ||||
log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=9) | log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=8) | ||||
if invitationpolicy is not None: | if invitationpolicy is not None: | ||||
for policy in invitationpolicy: | for policy in invitationpolicy: | ||||
if policy & ACT_REJECT: | if policy & ACT_REJECT: | ||||
reject = True | reject = True | ||||
break | break | ||||
if resource is not None and not reject: | if resource is not None and not reject: | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | def expunge_resource_calendar(mailbox): | ||||
imap.set_acl(targetfolder, conf.get(conf.get('kolab', 'imap_backend'), 'admin_login'), "lrswipkxtecda") | imap.set_acl(targetfolder, conf.get(conf.get('kolab', 'imap_backend'), 'admin_login'), "lrswipkxtecda") | ||||
imap.imap.m.select(targetfolder) | imap.imap.m.select(targetfolder) | ||||
typ, data = imap.imap.m.search(None, 'UNDELETED') | typ, data = imap.imap.m.search(None, 'UNDELETED') | ||||
for num in data[0].split(): | for num in data[0].split(): | ||||
log.debug( | log.debug( | ||||
_("Fetching message ID %r from folder %r") % (num, mailbox), | _("Fetching message ID %r from folder %r") % (num, mailbox), | ||||
level=9 | level=8 | ||||
) | ) | ||||
typ, data = imap.imap.m.fetch(num, '(RFC822)') | typ, data = imap.imap.m.fetch(num, '(RFC822)') | ||||
event_message = message_from_string(data[0][1]) | event_message = message_from_string(data[0][1]) | ||||
try: | try: | ||||
event = event_from_message(message_from_string(data[0][1])) | event = event_from_message(message_from_string(data[0][1])) | ||||
Show All 37 Lines | for resource in resources.keys(): | ||||
# sets the 'conflicting' flag and adds a list of conflicting events found | # sets the 'conflicting' flag and adds a list of conflicting events found | ||||
try: | try: | ||||
num_messages += read_resource_calendar(resources[resource], itip_events) | num_messages += read_resource_calendar(resources[resource], itip_events) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Failed to read resource calendar for %r: %r") % (resource, e)) | log.error(_("Failed to read resource calendar for %r: %r") % (resource, e)) | ||||
end = time.time() | end = time.time() | ||||
log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=9) | log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=8) | ||||
# For each resource (collections are first!) | # For each resource (collections are first!) | ||||
# check conflicts and either accept or decline the reservation request | # check conflicts and either accept or decline the reservation request | ||||
for resource in resource_dns: | for resource in resource_dns: | ||||
log.debug(_("Polling for resource %r") % (resource), level=9) | log.debug(_("Polling for resource %r") % (resource), level=8) | ||||
if not resources.has_key(resource): | if not resources.has_key(resource): | ||||
log.debug(_("Resource %r has been popped from the list") % (resource), level=9) | log.debug(_("Resource %r has been popped from the list") % (resource), level=8) | ||||
continue | continue | ||||
if not resources[resource].has_key('conflicting_events'): | if not resources[resource].has_key('conflicting_events'): | ||||
log.debug(_("Resource is a collection"), level=9) | log.debug(_("Resource is a collection"), level=8) | ||||
# check if there are non-conflicting collection members | # check if there are non-conflicting collection members | ||||
conflicting_members = [x for x in resources[resource]['uniquemember'] if resources[x]['conflict']] | conflicting_members = [x for x in resources[resource]['uniquemember'] if resources[x]['conflict']] | ||||
# found at least one non-conflicting member, remove the conflicting ones and continue | # found at least one non-conflicting member, remove the conflicting ones and continue | ||||
if len(conflicting_members) < len(resources[resource]['uniquemember']): | if len(conflicting_members) < len(resources[resource]['uniquemember']): | ||||
for member in conflicting_members: | for member in conflicting_members: | ||||
resources[resource]['uniquemember'] = [x for x in resources[resource]['uniquemember'] if x != member] | resources[resource]['uniquemember'] = [x for x in resources[resource]['uniquemember'] if x != member] | ||||
del resources[member] | del resources[member] | ||||
log.debug(_("Removed conflicting resources from %r: (%r) => %r") % ( | log.debug(_("Removed conflicting resources from %r: (%r) => %r") % ( | ||||
resource, conflicting_members, resources[resource]['uniquemember'] | resource, conflicting_members, resources[resource]['uniquemember'] | ||||
), level=9) | ), level=8) | ||||
else: | else: | ||||
# TODO: shuffle existing bookings of collection members in order | # TODO: shuffle existing bookings of collection members in order | ||||
# to make one available for the requested time | # to make one available for the requested time | ||||
pass | pass | ||||
continue | continue | ||||
if len(resources[resource]['conflicting_events']) > 0: | if len(resources[resource]['conflicting_events']) > 0: | ||||
log.debug(_("Conflicting events: %r for resource %r") % (resources[resource]['conflicting_events'], resource), level=9) | log.debug(_("Conflicting events: %r for resource %r") % (resources[resource]['conflicting_events'], resource), level=8) | ||||
done = False | done = False | ||||
# This is the event being conflicted with! | # This is the event being conflicted with! | ||||
for itip_event in itip_events: | for itip_event in itip_events: | ||||
# do not re-assign single occurrences to another resource | # do not re-assign single occurrences to another resource | ||||
if itip_event['recurrence-id'] is not None: | if itip_event['recurrence-id'] is not None: | ||||
continue | continue | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | def read_resource_calendar(resource_rec, itip_events): | ||||
resource_rec['conflict'] = False | resource_rec['conflict'] = False | ||||
resource_rec['conflicting_events'] = [] | resource_rec['conflicting_events'] = [] | ||||
resource_rec['existing_events'] = [] | resource_rec['existing_events'] = [] | ||||
mailbox = resource_rec['kolabtargetfolder'] | mailbox = resource_rec['kolabtargetfolder'] | ||||
log.debug( | log.debug( | ||||
_("Checking events in resource folder %r") % (mailbox), | _("Checking events in resource folder %r") % (mailbox), | ||||
level=9 | level=8 | ||||
) | ) | ||||
# set read ACLs for admin user | # set read ACLs for admin user | ||||
imap.set_acl(mailbox, conf.get(conf.get('kolab', 'imap_backend'), 'admin_login'), "lrs") | imap.set_acl(mailbox, conf.get(conf.get('kolab', 'imap_backend'), 'admin_login'), "lrs") | ||||
# might raise an exception, let that bubble | # might raise an exception, let that bubble | ||||
imap.imap.m.select(imap.folder_quote(mailbox)) | imap.imap.m.select(imap.folder_quote(mailbox)) | ||||
typ, data = imap.imap.m.search(None, 'UNDELETED') | typ, data = imap.imap.m.search(None, 'UNDELETED') | ||||
num_messages = len(data[0].split()) | num_messages = len(data[0].split()) | ||||
for num in data[0].split(): | for num in data[0].split(): | ||||
# For efficiency, makes the routine non-deterministic | # For efficiency, makes the routine non-deterministic | ||||
if resource_rec['conflict']: | if resource_rec['conflict']: | ||||
continue | continue | ||||
log.debug( | log.debug( | ||||
_("Fetching message UID %r from folder %r") % (num, mailbox), | _("Fetching message UID %r from folder %r") % (num, mailbox), | ||||
level=9 | level=8 | ||||
) | ) | ||||
typ, data = imap.imap.m.fetch(num, '(UID RFC822)') | typ, data = imap.imap.m.fetch(num, '(UID RFC822)') | ||||
try: | try: | ||||
msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) | msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("No UID found in IMAP response: %r") % (data[0][0])) | log.error(_("No UID found in IMAP response: %r") % (data[0][0])) | ||||
Show All 37 Lines | """ | ||||
Search the resources's calendar folder for the given event (by UID) | Search the resources's calendar folder for the given event (by UID) | ||||
""" | """ | ||||
global imap | global imap | ||||
event = None | event = None | ||||
master = None | master = None | ||||
mailbox = resource_rec['kolabtargetfolder'] | mailbox = resource_rec['kolabtargetfolder'] | ||||
log.debug(_("Searching %r for event %r") % (mailbox, uid), level=9) | log.debug(_("Searching %r for event %r") % (mailbox, uid), level=8) | ||||
try: | try: | ||||
imap.imap.m.select(imap.folder_quote(mailbox)) | imap.imap.m.select(imap.folder_quote(mailbox)) | ||||
typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) | typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid)) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Failed to access resource calendar:: %r") % (e)) | log.error(_("Failed to access resource calendar:: %r") % (e)) | ||||
return event | return event | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | """ | ||||
ACCEPTED and sends an iTip reply message to the organizer. | ACCEPTED and sends an iTip reply message to the organizer. | ||||
""" | """ | ||||
owner = get_resource_owner(resource) | owner = get_resource_owner(resource) | ||||
confirmation_required = False | confirmation_required = False | ||||
if not confirmed and owner: | if not confirmed and owner: | ||||
if invitationpolicy is None: | if invitationpolicy is None: | ||||
invitationpolicy = get_resource_invitationpolicy(resource) | invitationpolicy = get_resource_invitationpolicy(resource) | ||||
log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=9) | log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=8) | ||||
if invitationpolicy is not None: | if invitationpolicy is not None: | ||||
for policy in invitationpolicy: | for policy in invitationpolicy: | ||||
if policy & ACT_MANUAL and owner['mail']: | if policy & ACT_MANUAL and owner['mail']: | ||||
confirmation_required = True | confirmation_required = True | ||||
break | break | ||||
partstat = 'TENTATIVE' if confirmation_required else 'ACCEPTED' | partstat = 'TENTATIVE' if confirmation_required else 'ACCEPTED' | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | try: | ||||
), level=8) | ), level=8) | ||||
imap.imap.m.uid('store', msguid, '+FLAGS', '(\\Deleted)') | imap.imap.m.uid('store', msguid, '+FLAGS', '(\\Deleted)') | ||||
else: | else: | ||||
typ, data = imap.imap.m.search(None, '(HEADER SUBJECT "%s")' % uid) | typ, data = imap.imap.m.search(None, '(HEADER SUBJECT "%s")' % uid) | ||||
log.debug(_("Delete resource calendar object %r in %r: %r") % ( | log.debug(_("Delete resource calendar object %r in %r: %r") % ( | ||||
uid, resource['kolabtargetfolder'], data | uid, resource['kolabtargetfolder'], data | ||||
), level=9) | ), level=8) | ||||
for num in data[0].split(): | for num in data[0].split(): | ||||
imap.imap.m.store(num, '+FLAGS', '\\Deleted') | imap.imap.m.store(num, '+FLAGS', '\\Deleted') | ||||
imap.imap.m.expunge() | imap.imap.m.expunge() | ||||
return True | return True | ||||
except Exception, e: | except Exception, e: | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | def resource_record_from_email_address(email_address): | ||||
) | ) | ||||
resource_records = auth.find_resource(email_address) | resource_records = auth.find_resource(email_address) | ||||
if isinstance(resource_records, list): | if isinstance(resource_records, list): | ||||
if len(resource_records) > 0: | if len(resource_records) > 0: | ||||
log.debug(_("Resource record(s): %r") % (resource_records), level=8) | log.debug(_("Resource record(s): %r") % (resource_records), level=8) | ||||
else: | else: | ||||
log.debug(_("No resource (collection) records found for %r") % (email_address), level=9) | log.debug(_("No resource (collection) records found for %r") % (email_address), level=8) | ||||
elif isinstance(resource_records, basestring): | elif isinstance(resource_records, basestring): | ||||
resource_records = [ resource_records ] | resource_records = [ resource_records ] | ||||
log.debug(_("Resource record: %r") % (resource_records), level=8) | log.debug(_("Resource record: %r") % (resource_records), level=8) | ||||
return resource_records | return resource_records | ||||
def resource_records_from_itip_events(itip_events, recipient_email=None): | def resource_records_from_itip_events(itip_events, recipient_email=None): | ||||
""" | """ | ||||
Given a list of itip_events, determine which resources have been | Given a list of itip_events, determine which resources have been | ||||
invited as attendees and/or resources. | invited as attendees and/or resources. | ||||
""" | """ | ||||
global auth | global auth | ||||
if not auth: | if not auth: | ||||
auth = Auth() | auth = Auth() | ||||
auth.connect() | auth.connect() | ||||
resource_records = [] | resource_records = [] | ||||
log.debug(_("Raw itip_events: %r") % (itip_events), level=9) | log.debug(_("Raw itip_events: %r") % (itip_events), level=8) | ||||
attendees_raw = [] | attendees_raw = [] | ||||
for list_attendees_raw in [x for x in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], list)]]: | for list_attendees_raw in [x for x in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], list)]]: | ||||
attendees_raw.extend(list_attendees_raw) | attendees_raw.extend(list_attendees_raw) | ||||
for list_attendees_raw in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], basestring)]: | for list_attendees_raw in [y['attendees'] for y in itip_events if y.has_key('attendees') and isinstance(y['attendees'], basestring)]: | ||||
attendees_raw.append(list_attendees_raw) | attendees_raw.append(list_attendees_raw) | ||||
log.debug(_("Raw set of attendees: %r") % (attendees_raw), level=9) | log.debug(_("Raw set of attendees: %r") % (attendees_raw), level=8) | ||||
# TODO: Resources are actually not implemented in the format. We reset this | # TODO: Resources are actually not implemented in the format. We reset this | ||||
# list later. | # list later. | ||||
resources_raw = [] | resources_raw = [] | ||||
for list_resources_raw in [x for x in [y['resources'] for y in itip_events if y.has_key('resources')]]: | for list_resources_raw in [x for x in [y['resources'] for y in itip_events if y.has_key('resources')]]: | ||||
resources_raw.extend(list_resources_raw) | resources_raw.extend(list_resources_raw) | ||||
log.debug(_("Raw set of resources: %r") % (resources_raw), level=9) | log.debug(_("Raw set of resources: %r") % (resources_raw), level=8) | ||||
# consider organizer (in REPLY messages), too | # consider organizer (in REPLY messages), too | ||||
organizers_raw = [re.sub('\+[A-Za-z0-9=/-]+@', '@', str(y['organizer'])) for y in itip_events if y.has_key('organizer')] | organizers_raw = [re.sub('\+[A-Za-z0-9=/-]+@', '@', str(y['organizer'])) for y in itip_events if y.has_key('organizer')] | ||||
log.debug(_("Raw set of organizers: %r") % (organizers_raw), level=8) | log.debug(_("Raw set of organizers: %r") % (organizers_raw), level=8) | ||||
# TODO: We expect the format of an attendee line to literally be: | # TODO: We expect the format of an attendee line to literally be: | ||||
Show All 17 Lines | for attendee in attendees: | ||||
_resource_records = auth.find_resource(attendee) | _resource_records = auth.find_resource(attendee) | ||||
if isinstance(_resource_records, list): | if isinstance(_resource_records, list): | ||||
if len(_resource_records) > 0: | if len(_resource_records) > 0: | ||||
resource_records.extend(_resource_records) | resource_records.extend(_resource_records) | ||||
log.debug(_("Resource record(s): %r") % (_resource_records), level=8) | log.debug(_("Resource record(s): %r") % (_resource_records), level=8) | ||||
else: | else: | ||||
log.debug(_("No resource (collection) records found for %r") % (attendee), level=9) | log.debug(_("No resource (collection) records found for %r") % (attendee), level=8) | ||||
elif isinstance(_resource_records, basestring): | elif isinstance(_resource_records, basestring): | ||||
resource_records.append(_resource_records) | resource_records.append(_resource_records) | ||||
log.debug(_("Resource record: %r") % (_resource_records), level=8) | log.debug(_("Resource record: %r") % (_resource_records), level=8) | ||||
else: | else: | ||||
log.warning(_("Resource reservation made but no resource records found")) | log.warning(_("Resource reservation made but no resource records found")) | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | if not resource.has_key('kolabinvitationpolicy') or resource['kolabinvitationpolicy'] is None: | ||||
auth = Auth() | auth = Auth() | ||||
auth.connect() | auth.connect() | ||||
# get kolabinvitationpolicy attribute from collection | # get kolabinvitationpolicy attribute from collection | ||||
collections = auth.search_entry_by_attribute('uniquemember', resource['dn']) | collections = auth.search_entry_by_attribute('uniquemember', resource['dn']) | ||||
if not isinstance(collections, list): | if not isinstance(collections, list): | ||||
collections = [ (collections['dn'],collections) ] | collections = [ (collections['dn'],collections) ] | ||||
log.debug(_("Check collections %r for kolabinvitationpolicy attributes") % (collections), level=9) | log.debug(_("Check collections %r for kolabinvitationpolicy attributes") % (collections), level=8) | ||||
for dn,collection in collections: | for dn,collection in collections: | ||||
# ldap.search_entry_by_attribute() doesn't return the attributes lower-cased | # ldap.search_entry_by_attribute() doesn't return the attributes lower-cased | ||||
if collection.has_key('kolabInvitationPolicy'): | if collection.has_key('kolabInvitationPolicy'): | ||||
collection['kolabinvitationpolicy'] = collection['kolabInvitationPolicy'] | collection['kolabinvitationpolicy'] = collection['kolabInvitationPolicy'] | ||||
if collection.has_key('kolabinvitationpolicy'): | if collection.has_key('kolabinvitationpolicy'): | ||||
parse_kolabinvitationpolicy(collection) | parse_kolabinvitationpolicy(collection) | ||||
▲ Show 20 Lines • Show All 209 Lines • Show Last 20 Lines |