def update_container(self, obj): """ Removes duplicate users and updates containers. """ # Defaults ldap_archiving = api.portal.get_registry_record( name='operun.crm.ldap_archiving') # noqa result = self.search_for_user(obj) current_dn = self.generate_ldap_dn(obj) # Length should be 1 since we query by UID if len(result) > 1: for item in result: old_dn = item[0] if old_dn != current_dn: if ldap_archiving: self.archive_item(item) else: self.delete_ldap_object(old_dn) # If single result, check DN and update it elif len(result) == 1: old_dn = result[0][0] obj_cn = self.generate_ldap_cn(obj) obj_superior = self.generate_ldap_superior(obj) if old_dn != current_dn: try: self.connection.rename_s(old_dn, obj_cn, obj_superior) except ldap.LDAPError: logger.info( _('An error occurred, likely due to a conflicting DN.') ) # noqa else: logger.info(_('An error occurred in update_node_tree()'))
def update_container(self, obj): """ Removes duplicate users and updates containers. """ # Defaults ldap_archiving = api.portal.get_registry_record(name='operun.crm.ldap_archiving') # noqa result = self.search_for_user(obj) current_dn = self.generate_ldap_dn(obj) # Length should be 1 since we query by UID if len(result) > 1: for item in result: old_dn = item[0] if old_dn != current_dn: if ldap_archiving: self.archive_item(item) else: self.delete_ldap_object(old_dn) # If single result, check DN and update it elif len(result) == 1: old_dn = result[0][0] obj_cn = self.generate_ldap_cn(obj) obj_superior = self.generate_ldap_superior(obj) if old_dn != current_dn: try: self.connection.rename_s(old_dn, obj_cn, obj_superior) except ldap.LDAPError: logger.info(_('An error occurred, likely due to a conflicting DN.')) # noqa else: logger.info(_('An error occurred in update_node_tree()'))
def connect(self): """ Get config credentials and connect to LDAP server. Check sync switch in settings. """ sync_to_ldap = api.portal.get_registry_record( name='operun.crm.sync_to_ldap') # noqa ldap_server_uri = api.portal.get_registry_record( name='operun.crm.ldap_server_uri') # noqa ldap_service_user = api.portal.get_registry_record( name='operun.crm.ldap_service_user') # noqa ldap_service_pass = api.portal.get_registry_record( name='operun.crm.ldap_service_pass') # noqa if all([ sync_to_ldap, ldap_server_uri, ldap_service_user, ldap_service_pass ]): try: logger.info(_('Contacting LDAP server...')) self.connection = ldap.initialize(ldap_server_uri) self.connection.simple_bind_s(ldap_service_user, ldap_service_pass) # noqa except ldap.LDAPError: logger.info( _('Could not connect to {0}'.format( ldap_server_uri))) # noqa else: logger.info(_('Connected to {0}'.format(ldap_server_uri))) return self.connection else: logger.info(_('Check LDAP config.'))
class IOffer(model.Schema): """ Offer Content Type """ file = NamedBlobFile( title=_(u'Offer'), description=_(u'Please upload an offer'), required=False, )
class IInvoice(model.Schema): """ Invoice Content Type """ file = NamedBlobFile( title=_(u'Invoice'), description=_(u'Please upload an invoice'), required=False, )
def ldap_remove_stale_objects(self, container): """ Remove objects by UID from LDAP that no-longer exist in Plone. """ connection = self.connect() # Defaults ldap_objectclass_mapping = api.portal.get_registry_record( name='operun.crm.ldap_objectclass_mapping') # noqa accounts_dn = api.portal.get_registry_record( name='operun.crm.accounts_dn') # noqa users_dn = api.portal.get_registry_record(name='operun.crm.users_dn') content_type = container.Type() # Set Content-Type based variables if content_type == 'Accounts': portal_type = 'Account' ldap_dn = accounts_dn if content_type == 'Contacts': portal_type = 'Contact' ldap_dn = users_dn # Construct search query object_class = self.list_to_dict(ldap_objectclass_mapping)[portal_type] ldap_results = connection.search_s( ldap_dn, ldap.SCOPE_SUBTREE, '(objectClass={0})'.format(object_class)) # noqa # Loop through search results for item in ldap_results: item_dn = item[0] item_uid = item[1]['uid'][0] # If no object with UID in Plone, delete from LDAP if not api.content.find(portal_type=portal_type, UID=item_uid): self.delete_ldap_object(item_dn) logger.info(_( 'Deleted LDAP entry for {0}'.format(item_dn))) # noqa
def ldap_remove_stale_objects(self, container): """ Remove objects by UID from LDAP that no-longer exist in Plone. """ connection = self.connect() # Defaults ldap_objectclass_mapping = api.portal.get_registry_record(name='operun.crm.ldap_objectclass_mapping') # noqa accounts_dn = api.portal.get_registry_record(name='operun.crm.accounts_dn') # noqa users_dn = api.portal.get_registry_record(name='operun.crm.users_dn') content_type = container.Type() # Set Content-Type based variables if content_type == 'Accounts': portal_type = 'Account' ldap_dn = accounts_dn if content_type == 'Contacts': portal_type = 'Contact' ldap_dn = users_dn # Construct search query object_class = self.list_to_dict(ldap_objectclass_mapping)[portal_type] ldap_results = connection.search_s(ldap_dn, ldap.SCOPE_SUBTREE, '(objectClass={0})'.format(object_class)) # noqa # Loop through search results for item in ldap_results: item_dn = item[0] item_uid = item[1]['uid'][0] # If no object with UID in Plone, delete from LDAP if not api.content.find(portal_type=portal_type, UID=item_uid): self.delete_ldap_object(item_dn) logger.info(_('Deleted LDAP entry for {0}'.format(item_dn))) # noqa
def unbind(self): """ Unbind connection. """ try: self.connection.unbind() except AttributeError: pass else: logger.info(_('Disconnected from LDAP server...'))
def archive_item(self, item): """ If enabled, method will attempt to archive duplicate entry. """ archives_dn = api.portal.get_registry_record(name='operun.crm.archives_dn') # noqa old_dn = item[0] item_cn = item[1]['cn'][0] try: self.connection.rename_s(old_dn, 'cn={0} {1}'.format(item_cn, str(datetime.datetime.now())), archives_dn) # noqa except ldap.LDAPError: logger.info(_('An error occurred, item couldn\'t be archived.')) # noqa
def archive_item(self, item): """ If enabled, method will attempt to archive duplicate entry. """ archives_dn = api.portal.get_registry_record( name='operun.crm.archives_dn') # noqa old_dn = item[0] item_cn = item[1]['cn'][0] try: self.connection.rename_s(old_dn, 'cn={0} {1}'.format( item_cn, str(datetime.datetime.now())), archives_dn) # noqa except ldap.LDAPError: logger.info( _('An error occurred, item couldn\'t be archived.')) # noqa
def connect(self): """ Get config credentials and connect to LDAP server. Check sync switch in settings. """ sync_to_ldap = api.portal.get_registry_record(name='operun.crm.sync_to_ldap') # noqa ldap_server_uri = api.portal.get_registry_record(name='operun.crm.ldap_server_uri') # noqa ldap_service_user = api.portal.get_registry_record(name='operun.crm.ldap_service_user') # noqa ldap_service_pass = api.portal.get_registry_record(name='operun.crm.ldap_service_pass') # noqa if all([sync_to_ldap, ldap_server_uri, ldap_service_user, ldap_service_pass]): try: logger.info(_('Contacting LDAP server...')) self.connection = ldap.initialize(ldap_server_uri) self.connection.simple_bind_s(ldap_service_user, ldap_service_pass) # noqa except ldap.LDAPError: logger.info(_('Could not connect to {0}'.format(ldap_server_uri))) # noqa else: logger.info(_('Connected to {0}'.format(ldap_server_uri))) return self.connection else: logger.info(_('Check LDAP config.'))
def add_ldap_object(self, item=None): """ Create object in LDAP with content_type and UID. Get list of fields config using get_field_mapping method. Iterate over list and update each attribute. """ connection = self.connect() if connection: if not item: item = self.context mod_attrs = self.create_mod_attrs(item) ldap_dn = self.generate_ldap_dn(item) try: connection.add_s(ldap_dn, mod_attrs) except ldap.LDAPError: logger.info(_('An error occurred in add_ldap_object()')) self.unbind()
def update_ldap_object(self, item=None): """ Get list of attributes from get_fields_to_update method. Update target with constructed list of attributes and values. """ connection = self.connect() if connection: if not item: item = self.context mod_attrs = self.update_mod_attrs(item) ldap_dn = self.generate_ldap_dn(item) self.update_container(item) try: connection.modify_s(ldap_dn, mod_attrs) except ldap.LDAPError: logger.info(_('An error occurred in update_ldap_object()')) self.unbind()
def validate(self, value): """ Validate titles. """ super(TitleValidator, self).validate(value) context_portal_type = self.context.Type() if context_portal_type == 'Contacts': results = api.content.find(portal_type='Contact', Title=value) # noqa if results: raise zope.interface.Invalid( _(u'contact_title_form_validator_message', default=u'Display Name not unique!')) else: return True else: return True
def validate(self, value): """ Validate titles. """ super(TitleValidator, self).validate(value) context_portal_type = self.context.Type() if context_portal_type == 'Contacts': results = api.content.find(portal_type='Contact', Title=value) # noqa if results: raise zope.interface.Invalid( _(u'contact_title_form_validator_message', default=u'Display Name not unique!') ) else: return True else: return True
def ldap_update_attribute(self, item=None, field=None): """ Check if connection. If connection, update LDAP attribute. """ connection = self.connect() if connection: if not item: item = self.context ldap_dn = self.generate_ldap_dn(item) item = self.convert_to_object(item) content_type = item.Type() mod_attrs = [(self.get_mapped_field(content_type, field), str(getattr(item, field)))] # noqa try: connection.modify_s(ldap_dn, mod_attrs) except ldap.LDAPError: logger.info(_('An error occurred in ldap_update_attribute()')) self.unbind()
def delete_ldap_object(self, item=None): """ If object in LDAP, delete object. """ connection = self.connect() if connection: if not item: item = self.context ldap_dn = self.generate_ldap_dn(item) else: ldap_dn = item if not isinstance(item, str): ldap_dn = self.generate_ldap_dn(item) try: connection.delete(ldap_dn) except ldap.LDAPError: logger.info(_('An error occurred in delete_ldap_object()')) self.unbind()
class ModalEditForm(edit.DefaultEditForm): """ Edit only one field at a time. To be used in modals. """ template = ViewPageTemplateFile('templates/modal_edit.pt') @button.buttonAndHandler(_(u'Save'), name='save') def handleApply(self, action): # noqa # Override widget modes to ignore all other fields prefix = 'form.widgets.' field_ids = [k.split(prefix)[-1] for k in self.request.form.keys()] self.request.set('fields', field_ids) # Set all widgets to 'display' to prevent saving data self.set_all_widgets_mode(interfaces.DISPLAY_MODE) # Change the custom_form_fields to input-mode self.set_widgets_mode(field_ids, interfaces.INPUT_MODE) # Original code data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return self.applyChanges(data) IStatusMessage(self.request).addStatusMessage( self.success_message, 'info', ) # noqa self.request.response.redirect(self.nextURL()) notify(EditFinishedEvent(self.context)) def fields_info(self, fields=None): """ Get info about the fields and their modes from the query-string. """ results = [] if not fields: return if not isinstance(fields, list): fields = [fields] sorted_fields = [] schema_fields = self.fields.keys() for fieldname in schema_fields + fields: if fieldname not in sorted_fields and fieldname in fields: sorted_fields.append(fieldname) for fieldname in sorted_fields: fieldmode = interfaces.INPUT_MODE label = True mode = None if ':' in fieldname: values = fieldname.split(':') if len(values) == 2: fieldname, mode = values elif len(values) == 3: fieldname, mode, label = values if label.lower() in ['0', 'false', 'no']: label = False if mode and mode in [ interfaces.INPUT_MODE, interfaces.DISPLAY_MODE, interfaces.HIDDEN_MODE ]: # noqa fieldmode = str(mode) results.append({ 'fieldname': fieldname, 'fieldmode': fieldmode, 'label': label, }) return results def get_widget(self, fieldname=None, fieldmode=None, label=True, autofocus=False): # noqa fieldname = fieldname or self.request.get('fieldname') fieldmode = fieldmode or self.request.get( 'fieldmode') or interfaces.INPUT_MODE # noqa field_hooks = { 'textline-field': 'type=\"text\"', 'list-field': 'type=\"text\"', 'richtext-field': 'textarea', } label = label or self.request.get('label') if not fieldname: return widget = self.find_widget(fieldname) if widget: widget.mode = fieldmode fieldclass = widget.klass # Return wrapped or non-wrapped field element if not label: widget_view = widget.render else: widget_view = api.content.get_view('ploneform-render-widget', widget, self.request) # Apply autofocus attribute if autofocus and fieldclass: for key in field_hooks.keys(): if key in fieldclass: hook = field_hooks[key] widget_view = widget_view() widget_view = widget_view.replace( hook, hook + ' autofocus=\"\"', 1) # noqa return widget_view return widget_view() def set_all_widgets_mode(self, mode): for widget in self.widgets.values(): widget.mode = mode group_widgets = [ widget for group in self.groups for widget in group.widgets.values() ] # noqa for widget in group_widgets: widget.mode = mode def set_widgets_mode(self, field_ids, mode): for field_id in field_ids: widget = self.find_widget(field_id) if widget: widget.mode = mode def find_widget(self, field_id): """ Return a widget for any field in the schema and behaviors. """ widget = self.widgets.get(field_id, None) if not widget: for group in self.groups: widget = group.widgets.get(field_id, None) if widget: break if widget: return widget
# -*- coding: utf-8 -*- from operun.crm import MessageFactory as _ from zope.schema.vocabulary import SimpleTerm from zope.schema.vocabulary import SimpleVocabulary ACCOUNT_TYPES = SimpleVocabulary( [SimpleTerm(value=u'contact', title=_(u'Contact')), SimpleTerm(value=u'employee', title=_(u'Employee')), SimpleTerm(value=u'lead', title=_(u'Lead')), SimpleTerm(value=u'customer', title=_(u'Customer')), SimpleTerm(value=u'vendor', title=_(u'Vendor'))] )
class ISettings(Interface): """ Controlpanel fields. """ fieldset(_(u'settings_fieldset_server', default=u'Server'), fields=[ 'sync_to_ldap', 'ldap_server_uri', 'ldap_service_user', 'ldap_service_pass' ]) sync_to_ldap = schema.Bool( title=_(u'settings_sync_to_ldap_title', default=u'LDAP Syncing'), description=_( u'settings_sync_to_ldap_description', default= u'If enabled, the CRM will attempt an LDAP connection on sync events.' ), # noqa required=False, default=False, ) ldap_server_uri = schema.TextLine( title=_(u'settings_ldap_server_uri_title', default=u'LDAP Server URI'), default=u'ldap://127.0.0.1', required=False, ) ldap_service_user = schema.TextLine( title=_(u'settings_ldap_service_user_title', default=u'LDAP Service User'), required=False, default=u'cn=admin,dc=example,dc=com', ) ldap_service_pass = schema.Password( title=_(u'settings_ldap_service_pass_title', default=u'LDAP Service Password'), required=False, ) fieldset(_(u'settings_fieldset_dn', default=u'DN'), fields=['users_dn', 'groups_dn', 'accounts_dn', 'archives_dn']) users_dn = schema.TextLine( title=_(u'settings_users_dn_title', default=u'Users DN'), description=_( u'settings_users_dn_description', default=u'Include the users DN. e.g. ou=users,dc=example,dc=com' ), # noqa required=False, default=u'ou=users,dc=example,dc=com', ) groups_dn = schema.TextLine( title=_(u'settings_groups_dn_title', default=u'Groups DN'), description=_( u'settings_groups_dn_description', default=u'Include the groups DN. e.g. ou=groups,dc=example,dc=com' ), # noqa required=False, default=u'ou=groups,dc=example,dc=com', ) accounts_dn = schema.TextLine( title=_(u'settings_accounts_dn_title', default=u'Accounts DN'), description=_( u'settings_accounts_dn_description', default= u'Include the accounts DN. e.g. ou=accounts,dc=example,dc=com' ), # noqa required=False, default=u'ou=accounts,dc=example,dc=com', ) archives_dn = schema.TextLine( title=_(u'settings_archives_dn_title', default=u'Archives DN'), description=_( u'settings_archives_dn_description', default= u'Include the archives DN. e.g. ou=archives,dc=example,dc=com' ), # noqa required=False, default=u'ou=archives,dc=example,dc=com', ) fieldset(_(u'settings_fieldset_mapping', default=u'Mapping'), fields=[ 'ldap_field_mapping_contact', 'ldap_field_mapping_account', 'ldap_objectclass_mapping' ]) ldap_field_mapping_contact = schema.List( title=_(u'settings_ldap_field_mapping_contact_title', default=u'Contact Attribute Mapping'), description=_( u'settings_ldap_field_mapping_contact_description', default= u'Map Plone fields to their corresponding LDAP attributes. Plone|LDAP' ), # noqa value_type=schema.TextLine(), default=[ 'title|cn', 'email|mail', 'firstname|givenname', 'lastname|sn' ], required=False, ) ldap_field_mapping_account = schema.List( title=_(u'settings_ldap_field_mapping_account_title', default=u'Account Attribute Mapping'), description=_( u'settings_ldap_field_mapping_account_description', default= u'Map Plone fields to their corresponding LDAP attributes. Plone|LDAP' ), # noqa value_type=schema.TextLine(), default=['title|cn', 'billing_email|mail', 'ceo|givenname', 'type|sn'], required=False, ) ldap_objectclass_mapping = schema.List( title=_(u'settings_ldap_objectclass_mapping_title', default=u'Content-Type to objectClass Mapping'), description=_( u'settings_ldap_objectclass_mapping_description', default=u'Map Plone Content-Type to an LDAP objectClass. Plone|LDAP' ), # noqa value_type=schema.TextLine(), default=[ 'Group|posixGroup', 'Account|inetOrgPerson', 'Contact|inetOrgPerson' ], required=False, ) fieldset(_(u'settings_fieldset_other', default=u'Other'), fields=['manual_ldap_actions', 'ldap_archiving']) manual_ldap_actions = schema.Bool( title=_(u'settings_manual_ldap_actions_title', default=u'Manual LDAP Actions'), description=_( u'settings_manual_ldap_actions_description', default=u'Enable manual LDAP controls in the actions menu.' ), # noqa required=False, default=False, ) ldap_archiving = schema.Bool( title=_(u'settings_ldap_archiving_title', default=u'LDAP Archiving'), description=_( u'settings_ldap_archiving_description', default= u'If enabled, duplicate LDAP entries will be archived instead of deleted.' ), # noqa required=False, default=False, )
class SettingsEditForm(RegistryEditForm): schema = ISettings schema_prefix = 'operun.crm' label = _(u'crm_settings_title', default=u'CRM Settings')
class IAccount(model.Schema): """ Account Content Type """ title = schema.TextLine( title=_(u'Display Name'), required=True, ) type = schema.Choice( title=_(u'Account Type'), vocabulary=ACCOUNT_TYPES, required=False, default=u'customer', ) logo = NamedBlobImage( title=_(u'Company Logo'), description=_(u'Please upload an image'), required=False, ) ceo = schema.TextLine( title=_(u'CEO'), required=False, ) email = schema.TextLine( title=_(u'E-Mail'), required=False, ) phone = schema.TextLine( title=_(u'Phone'), required=False, ) fax = schema.TextLine( title=_(u'Fax'), required=False, ) website = schema.TextLine( title=_(u'Website'), required=False, ) project_reference = schema.TextLine( title=_(u'Project Reference'), description=_(u'A link to a Trac or Redmine project.'), required=False, ) # Address form.fieldset('address', label=_(u'Address'), fields=[ 'address', 'zip', 'city', ]) address = schema.TextLine( title=_(u'Address'), required=False, ) zip = schema.TextLine( title=_(u'ZIP'), required=False, ) city = schema.TextLine( title=_(u'City'), required=False, ) # Billing form.fieldset('billing', label=_(u'Billing'), fields=[ 'billing_email', 'billing_contact', ]) billing_email = schema.TextLine( title=_(u'Billing E-Mail'), required=False, ) billing_contact = RelationChoice( title=_(u'Billing Contact'), source=CatalogSource(portal_type='Contact'), required=False, ) # Notes form.fieldset('notes', label=_(u'Notes'), fields=[ 'text', ]) text = RichText( title=_(u'Notes'), required=False, )
# -*- coding: utf-8 -*- from operun.crm import MessageFactory as _ from zope.schema.vocabulary import SimpleTerm from zope.schema.vocabulary import SimpleVocabulary ACCOUNT_TYPES = SimpleVocabulary([ SimpleTerm(value=u'contact', title=_(u'Contact')), SimpleTerm(value=u'employee', title=_(u'Employee')), SimpleTerm(value=u'lead', title=_(u'Lead')), SimpleTerm(value=u'customer', title=_(u'Customer')), SimpleTerm(value=u'vendor', title=_(u'Vendor')) ])
class IContact(model.Schema): """ Contact Content Type """ title = schema.TextLine( title=_(u'Display Name'), required=True, ) firstname = schema.TextLine( title=_(u'Firstname'), required=True, ) lastname = schema.TextLine( title=_(u'Lastname'), required=True, ) type = schema.Choice( title=_(u'Contact Type'), vocabulary=ACCOUNT_TYPES, required=False, default=u'contact', ) # Job Title account = RelationChoice( title=_(u'Account'), source=CatalogSource(portal_type='Account'), required=False, ) # Department phone = schema.TextLine( title=_(u'Phone'), required=False, ) mobile = schema.TextLine( title=_(u'Mobile'), required=False, ) email = schema.TextLine( title=_(u'E-Mail'), required=False, ) businesscard = NamedBlobImage( title=_(u'Business Card'), description=_(u'Please upload an image'), required=False, ) notes = RichText( title=_(u'Notes'), required=False, )