diff --git a/management/univention-directory-manager-modules/modules/univention/admin/__init__.py b/management/univention-directory-manager-modules/modules/univention/admin/__init__.py index ed1239d..5707fa2 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/__init__.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/__init__.py @@ -198,7 +198,8 @@ def __init__( include_in_default_search=False, nonempty_is_default=False, readonly_when_synced=False, - size=None): + size=None, + copyable=False): self.short_description = short_description self.long_description = long_description @@ -228,6 +229,7 @@ def __init__( self.nonempty_is_default = nonempty_is_default self.readonly_when_synced = readonly_when_synced self.size = size + self.copyable = copyable def new(self): if self.multivalue: diff --git a/management/univention-directory-manager-modules/modules/univention/admin/handlers/groups/group.py b/management/univention-directory-manager-modules/modules/univention/admin/handlers/groups/group.py index f2d7946..c754564 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/handlers/groups/group.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/handlers/groups/group.py @@ -50,7 +50,7 @@ _ = translation.translate module = 'groups/group' -operations = ['add', 'edit', 'remove', 'search', 'move'] +operations = ['add', 'edit', 'remove', 'search', 'move', 'copy'] childs = 0 short_description = _('Group') long_description = '' @@ -113,7 +113,8 @@ may_change=True, identifies=False, default=('2', []), - options=['samba'] + options=['samba'], + copyable=True, ), 'sambaPrivileges': univention.admin.property( short_description=_('Samba privilege'), @@ -125,6 +126,7 @@ dontsearch=False, may_change=True, identifies=False, + copyable=True, ), 'adGroupType': univention.admin.property( short_description=_('AD group type'), @@ -138,6 +140,7 @@ may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'description': univention.admin.property( short_description=_('Description'), @@ -150,6 +153,7 @@ may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'users': univention.admin.property( short_description=_('Users'), @@ -162,6 +166,7 @@ dontsearch=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'hosts': univention.admin.property( short_description=_('Hosts'), @@ -175,6 +180,7 @@ dontsearch=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'mailAddress': univention.admin.property( short_description=_('Mail address'), @@ -188,6 +194,7 @@ dontsearch=False, identifies=False, readonly_when_synced=True, + copyable=True, ), 'memberOf': univention.admin.property( short_description=_('Member of'), @@ -200,6 +207,7 @@ dontsearch=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'nestedGroup': univention.admin.property( short_description=_('Groups'), @@ -212,6 +220,7 @@ dontsearch=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'allowedEmailUsers': univention.admin.property( short_description=_('Users that are allowed to send e-mails to the group'), @@ -223,6 +232,7 @@ may_change=True, dontsearch=True, identifies=False, + copyable=True, ), 'allowedEmailGroups': univention.admin.property( short_description=_('Groups that are allowed to send e-mails to the group'), @@ -234,6 +244,7 @@ may_change=True, dontsearch=True, identifies=False, + copyable=True, ) } diff --git a/management/univention-directory-manager-modules/modules/univention/admin/handlers/settings/extended_attribute.py b/management/univention-directory-manager-modules/modules/univention/admin/handlers/settings/extended_attribute.py index 949f50d..65caed7 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/handlers/settings/extended_attribute.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/handlers/settings/extended_attribute.py @@ -367,6 +367,17 @@ may_change=True, identifies=False ), + 'copyable': univention.admin.property( + short_description=_('Copyable'), + long_description=_('Values of this extended attribute are automatically filled into the form when copying a object.'), + syntax=univention.admin.syntax.boolean, + multivalue=False, + options=[], + required=False, + may_change=True, + identifies=False, + copyable=True, + ), } layout = [ @@ -418,6 +429,7 @@ ["valueRequired"], ["mayChange"], ["notEditable"], + ["copyable"], ]), ] @@ -446,6 +458,7 @@ mapping.register('valueRequired', 'univentionUDMPropertyValueRequired', None, univention.admin.mapping.ListToString) mapping.register('notEditable', 'univentionUDMPropertyValueNotEditable', None, univention.admin.mapping.ListToString) mapping.register('doNotSearch', 'univentionUDMPropertyDoNotSearch', None, univention.admin.mapping.ListToString) +mapping.register('copyable', 'univentionUDMPropertyCopyable', None, univention.admin.mapping.ListToString) mapping.register('version', 'univentionUDMPropertyVersion', None, univention.admin.mapping.ListToString) mapping.register('CLIName', 'univentionUDMPropertyCLIName', None, univention.admin.mapping.ListToString) mapping.register('options', 'univentionUDMPropertyOptions') diff --git a/management/univention-directory-manager-modules/modules/univention/admin/handlers/users/user.py b/management/univention-directory-manager-modules/modules/univention/admin/handlers/users/user.py index d5c9322..74d1d32 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/handlers/users/user.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/handlers/users/user.py @@ -73,7 +73,7 @@ class vacationResendDays(univention.admin.syntax.select): module = 'users/user' -operations = ['add', 'edit', 'remove', 'search', 'move'] +operations = ['add', 'edit', 'remove', 'search', 'move', 'copy'] template = 'settings/usertemplate' uid_umlauts_mixedcase = 0 @@ -168,6 +168,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'lastname': univention.admin.property( short_description=_('Last name'), @@ -179,6 +180,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'gecos': univention.admin.property( short_description=_('GECOS'), @@ -190,7 +192,8 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, default=' <:umlauts,strip>', identifies=False, - dontsearch=True + dontsearch=True, + copyable=True, ), 'displayName': univention.admin.property( short_description=_('Display name'), @@ -203,6 +206,7 @@ class vacationResendDays(univention.admin.syntax.select): default=' <:strip>', identifies=False, readonly_when_synced=True, + copyable=True, ), 'title': univention.admin.property( short_description=_('Title'), @@ -214,6 +218,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'sambaPrivileges': univention.admin.property( short_description=_('Samba privilege'), @@ -226,6 +231,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'description': univention.admin.property( short_description=_('Description'), @@ -237,6 +243,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'organisation': univention.admin.property( short_description=_('Organisation'), @@ -248,6 +255,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'userexpiry': univention.admin.property( short_description=_('Account expiry date'), @@ -259,6 +267,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, dontsearch=True, identifies=False, + copyable=True, ), 'passwordexpiry': univention.admin.property( short_description=_('Password expiry date'), @@ -272,6 +281,7 @@ class vacationResendDays(univention.admin.syntax.select): dontsearch=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'pwdChangeNextLogin': univention.admin.property( short_description=_('Change password on next login'), @@ -294,7 +304,8 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, - show_in_lists=True + show_in_lists=True, + copyable=True, ), 'locked': univention.admin.property( short_description=_('Locked login methods'), @@ -329,6 +340,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'e-mail': univention.admin.property( short_description=_('E-mail address'), @@ -339,7 +351,8 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, - default=[''] + default=[''], + copyable=True, ), 'postcode': univention.admin.property( short_description=_('Postal code'), @@ -351,6 +364,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'city': univention.admin.property( short_description=_('City'), @@ -362,6 +376,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'country': univention.admin.property( short_description=_('Country'), @@ -373,6 +388,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'phone': univention.admin.property( short_description=_('Telephone number'), @@ -384,6 +400,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'employeeNumber': univention.admin.property( short_description=_('Employee number'), @@ -395,6 +412,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'roomNumber': univention.admin.property( short_description=_('Room number'), @@ -405,6 +423,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'secretary': univention.admin.property( short_description=_('Superior'), @@ -415,6 +434,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'departmentNumber': univention.admin.property( short_description=_('Department number'), @@ -425,6 +445,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'employeeType': univention.admin.property( short_description=_('Employee type'), @@ -435,6 +456,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'homePostalAddress': univention.admin.property( short_description=_('Private postal address'), @@ -445,6 +467,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'homeTelephoneNumber': univention.admin.property( short_description=_('Private telephone number'), @@ -456,6 +479,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'mobileTelephoneNumber': univention.admin.property( short_description=_('Mobile phone number'), @@ -467,6 +491,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'pagerTelephoneNumber': univention.admin.property( short_description=_('Pager telephone number'), @@ -478,6 +503,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'birthday': univention.admin.property( short_description=_('Birthdate'), @@ -488,6 +514,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), 'unixhome': univention.admin.property( short_description=_('Unix home directory'), @@ -510,7 +537,8 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, - default='/bin/bash' + default='/bin/bash', + copyable=True, ), 'sambahome': univention.admin.property( short_description=_('Windows home path'), @@ -522,6 +550,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'scriptpath': univention.admin.property( short_description=_('Windows logon script'), @@ -533,6 +562,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'profilepath': univention.admin.property( short_description=_('Windows profile directory'), @@ -544,6 +574,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'homedrive': univention.admin.property( short_description=_('Windows home drive'), @@ -555,6 +586,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'sambaRID': univention.admin.property( short_description=_('Relative ID'), @@ -579,6 +611,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'primaryGroup': univention.admin.property( short_description=_('Primary group'), @@ -591,6 +624,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'mailHomeServer': univention.admin.property( short_description=_('Mail home server'), @@ -603,6 +637,7 @@ class vacationResendDays(univention.admin.syntax.select): dontsearch=False, may_change=True, identifies=False, + copyable=True, ), 'mailPrimaryAddress': univention.admin.property( short_description=_('Primary e-mail address'), @@ -628,6 +663,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=False, + copyable=True, ), 'mailForwardAddress': univention.admin.property( short_description=_('Forward e-mail address'), @@ -640,6 +676,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=False, + copyable=True, ), 'mailForwardCopyToSelf': univention.admin.property( short_description=_('Forwarding setting'), @@ -652,6 +689,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=False, + copyable=True, ), 'overridePWHistory': univention.admin.property( short_description=_('Override password history'), @@ -664,6 +702,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'overridePWLength': univention.admin.property( short_description=_('Override password check'), @@ -676,6 +715,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'homeShare': univention.admin.property( short_description=_('Home share'), @@ -687,6 +727,7 @@ class vacationResendDays(univention.admin.syntax.select): dontsearch=True, may_change=True, identifies=False, + copyable=True, ), 'homeSharePath': univention.admin.property( short_description=_('Home share path'), @@ -711,6 +752,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'sambaLogonHours': univention.admin.property( short_description=_('Permitted times for Windows logins'), @@ -723,6 +765,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, identifies=False, readonly_when_synced=True, + copyable=True, ), 'jpegPhoto': univention.admin.property( short_description=_("Picture of the user (JPEG format)"), @@ -734,6 +777,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, options=['person'], identifies=False, + copyable=True, ), 'userCertificate': univention.admin.property( short_description=_("PKI user certificate (DER format)"), @@ -745,6 +789,7 @@ class vacationResendDays(univention.admin.syntax.select): may_change=True, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerCountry': univention.admin.property( short_description=_('Issuer Country'), @@ -756,6 +801,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerState': univention.admin.property( short_description=_('Issuer State'), @@ -767,6 +813,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerLocation': univention.admin.property( short_description=_('Issuer Location'), @@ -778,6 +825,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerOrganisation': univention.admin.property( short_description=_('Issuer Organisation'), @@ -789,6 +837,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerOrganisationalUnit': univention.admin.property( short_description=_('Issuer Organisational Unit'), @@ -800,6 +849,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerCommonName': univention.admin.property( short_description=_('Issuer Common Name'), @@ -811,6 +861,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateIssuerMail': univention.admin.property( short_description=_('Issuer Mail'), @@ -822,6 +873,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectCountry': univention.admin.property( short_description=_('Subject Country'), @@ -833,6 +885,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectState': univention.admin.property( short_description=_('Subject State'), @@ -844,6 +897,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectLocation': univention.admin.property( short_description=_('Subject Location'), @@ -855,6 +909,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectOrganisation': univention.admin.property( short_description=_('Subject Organisation'), @@ -866,6 +921,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectOrganisationalUnit': univention.admin.property( short_description=_('Subject Organisational Unit'), @@ -877,6 +933,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectCommonName': univention.admin.property( short_description=_('Subject Common Name'), @@ -888,6 +945,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSubjectMail': univention.admin.property( short_description=_('Subject Mail'), @@ -899,6 +957,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateDateNotBefore': univention.admin.property( short_description=_('Valid from'), @@ -910,6 +969,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateDateNotAfter': univention.admin.property( short_description=_('Valid until'), @@ -921,6 +981,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateVersion': univention.admin.property( short_description=_('Version'), @@ -932,6 +993,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'certificateSerial': univention.admin.property( short_description=_('Serial'), @@ -943,6 +1005,7 @@ class vacationResendDays(univention.admin.syntax.select): editable=False, options=['pki'], identifies=False, + copyable=True, ), 'umcProperty': univention.admin.property( short_description=_('UMC user preferences'), @@ -953,6 +1016,7 @@ class vacationResendDays(univention.admin.syntax.select): required=False, may_change=True, identifies=False, + copyable=True, ), } diff --git a/management/univention-directory-manager-modules/modules/univention/admin/modules.py b/management/univention-directory-manager-modules/modules/univention/admin/modules.py index acbb584..707fdda 100644 --- a/management/univention-directory-manager-modules/modules/univention/admin/modules.py +++ b/management/univention-directory-manager-modules/modules/univention/admin/modules.py @@ -312,6 +312,8 @@ def update_extended_attributes(lo, module, position): # value is editable (only via hooks or direkt module.info[] access) editable = attrs.get('univentionUDMPropertyValueNotEditable', ['0'])[0] not in ['1', 'TRUE'] + copyable = attrs.get('univentionUDMPropertyCopyable', ['0'])[0] not in ['1', 'TRUE'] + # value is required valueRequired = (attrs.get('univentionUDMPropertyValueRequired', ['0'])[0].upper() in ['1', 'TRUE']) @@ -368,7 +370,8 @@ def update_extended_attributes(lo, module, position): dontsearch=doNotSearch, identifies=False, default=propertyDefault, - editable=editable + editable=editable, + copyable=copyable, ) # add LDAP mapping diff --git a/management/univention-ldap/schema/custom-attribute.schema b/management/univention-ldap/schema/custom-attribute.schema index aa5b3a7..0a7c242 100644 --- a/management/univention-ldap/schema/custom-attribute.schema +++ b/management/univention-ldap/schema/custom-attribute.schema @@ -285,6 +285,13 @@ attributetype ( 1.3.6.1.4.1.10176.200.130 NAME 'univentionUDMPropertyLayoutDisab SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) +attributetype ( 1.3.6.1.4.1.10176.200.131 NAME 'univentionUDMPropertyCopyable' + DESC 'defines if this attribute is copyable in UMC' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) + + objectclass ( 1.3.6.1.4.1.10176.200.199 NAME 'univentionUDMProperty' DESC ' defines a custom attribute for use in univention directory manager ' @@ -319,7 +326,8 @@ objectclass ( 1.3.6.1.4.1.10176.200.199 univentionUDMPropertyLayoutGroupName $ univentionUDMPropertyTranslationGroupName $ univentionUDMPropertyLayoutGroupPosition $ - univentionUDMPropertyLayoutDisable + univentionUDMPropertyLayoutDisable $ + univentionUDMPropertyCopyable ) ) diff --git a/management/univention-ldap/scripts/ldap_setup_index b/management/univention-ldap/scripts/ldap_setup_index index 6104c22..88aaab4 100755 --- a/management/univention-ldap/scripts/ldap_setup_index +++ b/management/univention-ldap/scripts/ldap_setup_index @@ -251,6 +251,7 @@ UDM_PROP_ATTRS = set(( 'univentionUDMPropertyValueMayChange', 'univentionUDMPropertyValueRequired', 'univentionUDMPropertyVersion', + 'univentionUDMPropertyCopyable', )) RECOMMENDED_LDAP_INDEX = { @@ -364,6 +365,7 @@ RECOMMENDED_LDAP_INDEX = { 'univentionUDMPropertyLongDescription', 'univentionUDMPropertyShortDescription', 'zoneName', + 'univentionUDMPropertyCopyable', )), 'approx': set(( 'cn', diff --git a/management/univention-management-console-module-udm/umc/js/udm.js b/management/univention-management-console-module-udm/umc/js/udm.js index da8a781..1fc5a5d 100644 --- a/management/univention-management-console-module-udm/umc/js/udm.js +++ b/management/univention-management-console-module-udm/umc/js/udm.js @@ -277,13 +277,13 @@ define([ // check whether we need to open directly the detail page of a given or a new object if (this.openObject) { - this._loadUCRVariables().then(lang.hitch(this, 'createDetailPage', + this._loadUCRVariables().then(lang.hitch(this, 'createDetailPage', 'edit', this.openObject.objectType, this.openObject.objectDN, undefined, true, this.openObject.note) ); return; // do not render the search page } if (this.newObject) { - this._loadUCRVariables().then(lang.hitch(this, 'createDetailPage', + this._loadUCRVariables().then(lang.hitch(this, 'createDetailPage', 'add', this.newObject.objectType, undefined, this.newObject, true, this.newObject.note) ); return; // do not render the search page @@ -366,7 +366,7 @@ define([ var objType = state.shift(); var ldapName = state.length > 1 ? state : state[0]; this._loadUCRVariables().then(lang.hitch(this, function() { - this.createDetailPage(objType, ldapName); + this.createDetailPage('edit', objType, ldapName); })); } this._set('moduleState', _state); @@ -684,7 +684,7 @@ define([ canExecute: lang.hitch(this, '_canEdit'), callback: lang.hitch(this, function(ids, items) { if (items.length == 1 && items[0].objectType) { - this.createDetailPage(items[0].objectType, ids[0]); + this.createDetailPage('edit', items[0].objectType, ids[0]); } else if (items.length >= 1 && items[0].objectType) { // make sure that all objects do have the same type var sameType = true; @@ -698,7 +698,7 @@ define([ } // everything ok, load detail page - this.createDetailPage(items[0].objectType, ids); + this.createDetailPage('edit', items[0].objectType, ids); } }) }, { @@ -742,6 +742,25 @@ define([ callback: lang.hitch(this, function(ids, objects) { this.moveObjects(objects); }) + }, { + name: 'copy', + label: _('Copy'), + description: _('Create a copy of the LDAP object.'), + isMultiAction: false, + canExecute: lang.hitch(this, '_canCopy'), + callback: lang.hitch(this, function(ids, items) { + this._showNewObjectDialog({ + args: { + wizardsDisabled: true, + defaultObjectType: items[0].objectType, + showObjectType: false, + showObjectTemplate: false + }, + callback: lang.hitch(this, function(options) { + this.createDetailPage('copy', items[0].objectType, ids[0], options); + }) + }); + }) }]; if ('navigation' !== this.moduleFlavor && this._reports.length) { @@ -1145,7 +1164,7 @@ define([ label: _('Edit'), iconClass: 'umcIconEdit', onClick: lang.hitch(this, function() { - this.createDetailPage(this._navContextItem.objectType, this._navContextItem.id); + this.createDetailPage('edit', this._navContextItem.objectType, this._navContextItem.id); }) })); menu.addChild(this._menuDelete = new MenuItem({ @@ -1207,6 +1226,10 @@ define([ return item.$operations$.indexOf('move') !== -1; }, + _canCopy: function(item) { + return item.$operations$.indexOf('copy') !== -1; + }, + _canDelete: function(item) { if (tools.isTrue(this._ucr['ad/member'])) { return -1 === array.indexOf(item.$flags$, 'synced'); @@ -1794,6 +1817,15 @@ define([ }, showNewObjectDialog: function() { + this._showNewObjectDialog({ + args: {}, + callback: lang.hitch(this, function(options) { + this.createDetailPage('add', options.objectType, undefined, options); + }) + }); + }, + + _showNewObjectDialog: function(args) { // summary: // Open a user dialog for creating a new LDAP object. @@ -1821,7 +1853,7 @@ define([ // open the dialog var onHandlerRegistered = new Deferred(); - this._newObjectDialog = new NewObjectDialog({ + this._newObjectDialog = new NewObjectDialog(lang.mixin({ addNotification: lang.hitch(this, 'addNotification'), umcpCommand: lang.hitch(this, 'umcpCommand'), wizardsDisabled: tools.isTrue(this._wizardsDisabled), @@ -1831,10 +1863,8 @@ define([ selectedContainer: selectedContainer, selectedSuperordinate: superordinate, defaultObjectType: this._ucr['directory/manager/web/modules/' + this.moduleFlavor + '/add/default'] || null - }); - this._newObjectDialog.on('FirstPageFinished', lang.hitch(this, function(options) { - this.createDetailPage(options.objectType, undefined, options); - })); + }, args.args)); + this._newObjectDialog.on('FirstPageFinished', args.callback); this._newObjectDialog.on('Done', lang.hitch(this, function() { if (this._newObjectDialog) { this._newObjectDialog.destroyRecursive(); @@ -1880,6 +1910,7 @@ define([ } this._setDetailPage( + 'edit', this._preloadedObjectType, this._ldapNameDeferred, /*newObjectOptions*/ null, @@ -1888,7 +1919,7 @@ define([ ); }, - _setDetailPage: function(objectType, ldapName, newObjOptions, /*Boolean*/ isClosable, /*String*/ note) { + _setDetailPage: function(operation, objectType, ldapName, newObjOptions, /*Boolean*/ isClosable, /*String*/ note) { this._destroyDetailPage(); var cssClass = this.moduleFlavor == 'users/user' ? 'umcUDMUsersModule' : ''; this._detailPage = new DetailPage({ @@ -1899,6 +1930,7 @@ define([ moduleStore: this.moduleStore, moduleFlavor: this.moduleFlavor, objectType: objectType, + operation: operation, ldapBase: this._ucr['ldap/base'], ldapName: ldapName, newObjectOptions: newObjOptions, @@ -1913,17 +1945,20 @@ define([ } }, - createDetailPage: function(objectType, ldapName, newObjOptions, /*Boolean?*/ isClosable, /*String?*/ note) { + createDetailPage: function(operation, objectType, ldapName, newObjOptions, /*Boolean?*/ isClosable, /*String?*/ note) { // summary: // Creates and views the detail page for editing LDAP objects if it doesn't exists. Afterwards it opens the detailpage. if (!this._ldapNameDeferred) { - this._preloadDetailPage(); + this._preloadDetailPage(operation); } - if (this._detailPage && this._preloadedObjectType == this.moduleFlavor && ldapName && !(ldapName instanceof Array)) { + if (operation === 'copy') { + this._setDetailPage(operation, objectType, ldapName, newObjOptions, isClosable, note); + this._ldapNameDeferred.resolve(ldapName); + } else if (this._detailPage && this._preloadedObjectType == this.moduleFlavor && ldapName && !(ldapName instanceof Array)) { // use pre-rendered detail page when loading a (single) object this._ldapNameDeferred.resolve(ldapName); } else { - this._setDetailPage(objectType, ldapName, newObjOptions, isClosable, note); + this._setDetailPage(operation, objectType, ldapName, newObjOptions, isClosable, note); this._ldapNameDeferred.resolve(null); } diff --git a/management/univention-management-console-module-udm/umc/js/udm/DetailPage.js b/management/univention-management-console-module-udm/umc/js/udm/DetailPage.js index 3caf4ca..442882b 100644 --- a/management/univention-management-console-module-udm/umc/js/udm/DetailPage.js +++ b/management/univention-management-console-module-udm/umc/js/udm/DetailPage.js @@ -26,7 +26,7 @@ * /usr/share/common-licenses/AGPL-3; if not, see * . */ -/*global require,define,setTimeout,dijit,window,console*/ +/*global require,define,setTimeout,window,console*/ define([ "dojo/_base/declare", @@ -101,6 +101,10 @@ define([ // Flavor of the module moduleFlavor: this.moduleFlavor, + // operation: String + // One of 'add', 'edit', 'copy' + operation: null, + // objectType: String // The object type of the LDAP object that is edited. objectType: null, @@ -278,7 +282,7 @@ define([ } return all({ - object: this.moduleStore.get(this.ldapName), + object: this.getObject(this.ldapName), formBuilt: formBuiltDeferred }).then(lang.hitch(this, function(result) { // save the original data we received from the server @@ -304,20 +308,31 @@ define([ }, this); })); - // var objecttype = vals.$labelObjectType$; - var path = tools.ldapDn2Path( this.ldapName, this.ldapBase); - var objecttype = _('Type: %(type)s', { type: vals.$labelObjectType$ }); - var position = _('Position: %(path)s', { path: path }); - var position_text = lang.replace('{0}
{1}', [objecttype, position]); - array.forEach(this._tabs.getChildren(), lang.hitch(this, function(child) { - if (child.position_text) { - child.position_text.set('content', position_text); - } - })); + if (this.operation === 'add') { // don't show when copying objects + // var objecttype = vals.$labelObjectType$; + var path = tools.ldapDn2Path( this.ldapName, this.ldapBase); + var objecttype = _('Type: %(type)s', { type: vals.$labelObjectType$ }); + var position = _('Position: %(path)s', { path: path }); + var position_text = lang.replace('{0}
{1}', [objecttype, position]); + array.forEach(this._tabs.getChildren(), lang.hitch(this, function(child) { + if (child.position_text) { + child.position_text.set('content', position_text); + } + })); + } return this._form.ready(); })); }, + getObject: function(dn) { + if (this.operation === 'copy') { + return this.umcpCommand('udm/copy', [dn], undefined, this.moduleFlavor).then(function(response) { + return response.result[0]; + }); + } + return this.moduleStore.get(dn); + }, + ready: function() { return this._pageRenderedDeferred; }, @@ -364,7 +379,7 @@ define([ }, _notifyAboutAutomaticChanges: function() { - if (!this.ldapName || this._multiEdit) { + if (this.operation === 'add' || this.operation === 'copy' || this._multiEdit) { // ignore creation of a new object as well as the multi edit mode return; } @@ -427,6 +442,11 @@ define([ this._policyDeferred.resolve(); })); })); + if (this.operation === 'copy') { + this._loadPolicies(policies).then(lang.hitch(this, function() { + this._policyDeferred.resolve(); + })); + } } else { // in case there are no policies, we use a dummy Deferred object this._policyDeferred.resolve(); @@ -554,7 +574,7 @@ define([ }, this); // make sure that the widget use the flavored umcpCommand - array.forEach( newProperties, function( iprop) { + array.forEach(newProperties, function(iprop) { iprop.umcpCommand = this.umcpCommand; }, this); @@ -654,11 +674,11 @@ define([ } // handle editable items - if ( iprop.readonly) { + if (iprop.readonly) { iprop.disabled = true; } else { if (iprop.disabled !== true) { - iprop.disabled = this.ldapName === undefined ? false : ! iprop.editable; + iprop.disabled = this.operation === 'add' ? false : ! iprop.editable; } } if (this._multiEdit && iprop.identifies) { @@ -754,8 +774,8 @@ define([ // the following checks are only necessary for the AD member mode when(this.ldapName, lang.hitch(this, function(ldapName){ this.ldapName = ldapName; - if (!ldapName) { - // new object / multiEdit... + if (!ldapName || this.operation === 'copy') { + // new object / multiEdit / copy... deferred.resolve(properties); return; } @@ -821,7 +841,7 @@ define([ return text; }); - if (!this.active_directory_enabled() || !this.ldapName || !this.isSyncedObject) { + if (!this.active_directory_enabled() || this.operation === 'add' || !this.isSyncedObject) { return; } var value = ''; @@ -852,7 +872,7 @@ define([ }, _prepareOptions: function(properties, layout, template, formBuiltDeferred) { - var isNewObject = !this.ldapName; + var isNewObject = this.operation === 'add'; var _getOptionProperty = function(properties) { var result = array.filter(properties, function(item) { @@ -1142,7 +1162,7 @@ define([ }, buildTemplate: function(_template, properties, widgets) { - if (this.ldapName || this._multiEdit) { + if (this.operation !== 'add' || this._multiEdit) { return; } @@ -1214,7 +1234,7 @@ define([ }); var createLabel = ''; - if (this.newObjectOptions) { + if (this.operation === 'add' || this.operation === 'copy') { createLabel = _createLabelText(); } else { createLabel = _('Save'); @@ -1731,7 +1751,7 @@ define([ this.moduleStore.put(ivals); }, this); deferred = transaction.commit(); - } else if (this.newObjectOptions) { + } else if (this.operation === 'add' || this.operation === 'copy') { deferred = this.moduleStore.add(vals, this.newObjectOptions); } else { deferred = this.moduleStore.put(vals); @@ -1885,7 +1905,7 @@ define([ newVals[iname] = iwidget.get('value'); } })); - } else if (this.newObjectOptions) { + } else if (this.operation === 'add' || this.operation === 'copy') { // get only non-empty values or values of type 'boolean' tools.forIn(vals, lang.hitch(this, function(iname, ival) { if (typeof(ival) == 'boolean' || (!(ival instanceof Array && !ival.length) && ival)) { @@ -1919,7 +1939,7 @@ define([ confirmClose: function() { topic.publish('/umc/actions', 'udm', this._parentModule.moduleFlavor, 'edit', 'cancel'); - if (!this.newObjectOptions && this.haveVisibleValuesChanged()) { + if (this.operation === 'edit' && this.haveVisibleValuesChanged()) { return dialog.confirm(_('There are unsaved changes. Are you sure to cancel?'), [{ label: _('Continue editing'), name: 'cancel' diff --git a/management/univention-management-console-module-udm/umc/js/udm/NewObjectDialog.js b/management/univention-management-console-module-udm/umc/js/udm/NewObjectDialog.js index 0fd5afe..059200f 100644 --- a/management/univention-management-console-module-udm/umc/js/udm/NewObjectDialog.js +++ b/management/univention-management-console-module-udm/umc/js/udm/NewObjectDialog.js @@ -74,6 +74,9 @@ define([ // The object type that is selected by default. defaultObjectType: null, + showObjectType: true, + showObjectTemplate: true, + autofocus: false, // interferes with Wizard.autoFocus _notificationText: null, @@ -148,7 +151,9 @@ define([ moduleFlavor: this.moduleFlavor, umcpCommand: this.umcpCommand, selectedContainer: this.selectedContainer, - selectedSuperordinate: this.selectedSuperordinate + selectedSuperordinate: this.selectedSuperordinate, + showObjectTemplate: this.showObjectTemplate, + showObjectType: this.showObjectType }); this._preWizard.canContinue().then(lang.hitch(this, function() { diff --git a/management/univention-management-console-module-udm/umc/js/udm/wizards/FirstPageWizard.js b/management/univention-management-console-module-udm/umc/js/udm/wizards/FirstPageWizard.js index 4595dd3..399ef5a 100644 --- a/management/univention-management-console-module-udm/umc/js/udm/wizards/FirstPageWizard.js +++ b/management/univention-management-console-module-udm/umc/js/udm/wizards/FirstPageWizard.js @@ -48,6 +48,9 @@ define([ _canContinue: null, // deferred which indicates if any of the pages in this wizard should be displayed or not + showObjectType: true, + showObjectTemplate: true, + postMixInProperties: function() { this.inherited(arguments); this._canContinue = new Deferred(); @@ -388,6 +391,7 @@ define([ label: _('Type'), description: _('The exact object type of the new LDAP object.'), autoHide: true, + visible: this.showObjectType, depends: selectedContainer ? [] : ['container'/*, 'superordinate'*/], dynamicValues: lang.hitch(this, function() { var containerWidget = this.getWidget('firstPage', 'container'); @@ -404,7 +408,9 @@ define([ }), size: 'Two' }); - layout.push('objectType'); + if (this.showObjectType) { + layout.push('objectType'); + } // templates widgets.push({ @@ -415,6 +421,7 @@ define([ value: this.defaultTemplate, depends: 'objectType', autoHide: true, + visible: this.showObjectTemplate, umcpCommand: this.umcpCommand, dynamicValues: lang.hitch(this, function(options) { return this.moduleCache.getTemplates(options.objectType).then(function(result) { @@ -425,7 +432,9 @@ define([ staticValues: [{id: 'None', label: _('None')}], size: 'Two' }); - layout.push('objectTemplate'); + if (this.showObjectTemplate) { + layout.push('objectTemplate'); + } if (this.moduleFlavor === 'navigation') { layout = ['container', 'container_help', 'objectType', 'objectTemplate']; diff --git a/management/univention-management-console-module-udm/umc/python/udm/__init__.py b/management/univention-management-console-module-udm/umc/python/udm/__init__.py index 4c5c3e3..3527509 100644 --- a/management/univention-management-console-module-udm/umc/python/udm/__init__.py +++ b/management/univention-management-console-module-udm/umc/python/udm/__init__.py @@ -455,47 +455,56 @@ def get(self, request): return: [ { '$dn$' : , }, ... ] """ - def _thread(request): - result = [] - for ldap_dn in request.options: - if request.flavor == 'users/self': - ldap_dn = self._user_dn - module = get_module(request.flavor, ldap_dn) - if module is None: - raise ObjectDoesNotExist(ldap_dn) - else: - obj = module.get(ldap_dn) - if obj: - obj.set_defaults = True - for name, p in obj.descriptions.items(): - if obj.has_key(name) and obj.descriptions[name].default(obj): # noqa: W601 - obj[name] # __getitem__ sets default value - props = obj.info - for passwd in module.password_properties: - if passwd in props: - del props[passwd] - props['$dn$'] = obj.dn - props['$options$'] = {} - for opt in module.get_options(udm_object=obj): - props['$options$'][opt['id']] = opt['value'] - props['$policies$'] = {} - for policy in obj.policies: - pol_mod = get_module(None, policy) - if pol_mod and pol_mod.name: - props['$policies$'].setdefault(pol_mod.name, []).append(policy) - props['$labelObjectType$'] = module.title - props['$flags$'] = obj.oldattr.get('univentionObjectFlag', []) - props['$operations$'] = module.operations - props['$references$'] = module.get_references(ldap_dn) - result.append(props) - else: - MODULE.process('The LDAP object for the LDAP DN %s could not be found' % ldap_dn) - return result - MODULE.info('Starting thread for udm/get request') - thread = notifier.threads.Simple('Get', notifier.Callback(_thread, request), notifier.Callback(self.thread_finished_callback, request)) + thread = notifier.threads.Simple('Get', notifier.Callback(self._get, request), notifier.Callback(self.thread_finished_callback, request)) thread.run() + def copy(self, request): + thread = notifier.threads.Simple('Copy', notifier.Callback(self._get, request, True), notifier.Callback(self.thread_finished_callback, request)) + thread.run() + + def _get(self, request, copy=False): + result = [] + for ldap_dn in request.options: + if request.flavor == 'users/self': + ldap_dn = self._user_dn + module = get_module(request.flavor, ldap_dn) + if module is None: + raise ObjectDoesNotExist(ldap_dn) + else: + obj = module.get(ldap_dn) + if obj: + if copy: + for name, p in obj.descriptions.items(): + if not p.copyable: + obj.info.pop(name, None) + obj.set_defaults = True + for name, p in obj.descriptions.items(): + if obj.has_key(name) and obj.descriptions[name].default(obj): # noqa: W601 + obj[name] # __getitem__ sets default value + props = obj.info + for passwd in module.password_properties: + if passwd in props: + del props[passwd] + if not copy: + props['$dn$'] = obj.dn + props['$options$'] = {} + for opt in module.get_options(udm_object=obj): + props['$options$'][opt['id']] = opt['value'] + props['$policies$'] = {} + for policy in obj.policies: + pol_mod = get_module(None, policy) + if pol_mod and pol_mod.name: + props['$policies$'].setdefault(pol_mod.name, []).append(policy) + props['$labelObjectType$'] = module.title + props['$flags$'] = obj.oldattr.get('univentionObjectFlag', []) + props['$operations$'] = module.operations + props['$references$'] = module.get_references(ldap_dn) + result.append(props) + else: + MODULE.process('The LDAP object for the LDAP DN %s could not be found' % ldap_dn) + return result + @sanitize( objectPropertyValue=LDAPSearchSanitizer( add_asterisks=ADD_ASTERISKS, diff --git a/management/univention-management-console-module-udm/umc/udm.xml b/management/univention-management-console-module-udm/umc/udm.xml index cc63ff7..00a8740 100644 --- a/management/univention-management-console-module-udm/umc/udm.xml +++ b/management/univention-management-console-module-udm/umc/udm.xml @@ -116,6 +116,7 @@ +