Univention Bugzilla – Attachment 8932 Details for
Bug 1567
Copy UDM objects
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch
1567.patch (text/plain), 46.99 KB, created by
Florian Best
on 2017-06-19 19:40 CEST
(
hide
)
Description:
patch
Filename:
MIME Type:
Creator:
Florian Best
Created:
2017-06-19 19:40 CEST
Size:
46.99 KB
patch
obsolete
>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='<firstname> <lastname><: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='<firstname> <lastname><: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=['<mailPrimaryAddress>'] >+ default=['<mailPrimaryAddress>'], >+ 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 > * <http://www.gnu.org/licenses/>. > */ >-/*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: <i>%(type)s</i>', { type: vals.$labelObjectType$ }); >- var position = _('Position: <i>%(path)s</i>', { path: path }); >- var position_text = lang.replace('{0}<br>{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: <i>%(type)s</i>', { type: vals.$labelObjectType$ }); >+ var position = _('Position: <i>%(path)s</i>', { path: path }); >+ var position_text = lang.replace('{0}<br>{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$' : <LDAP DN>, <object properties> }, ... ] > """ > >- 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 @@ > <command name="udm/add" function="add" /> > <command name="udm/remove" function="remove" /> > <command name="udm/get" function="get" /> >+ <command name="udm/copy" function="copy" /> > <command name="udm/move" function="move" /> > <command name="udm/progress" function="progress" /> > <command name="udm/validate" function="validate" />
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Actions:
View
|
Diff
Attachments on
bug 1567
:
8917
| 8932