diff options
author | Simo Sorce <ssorce@redhat.com> | 2007-09-04 15:40:24 -0400 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2007-09-04 15:40:24 -0400 |
commit | 48e70a4bf03e18c305d4a29fd0e0f83b8596c36b (patch) | |
tree | 9693db4f636a1a526db2dccae0d3a5c8dd90b4b1 | |
parent | abeda55e34ff19d34bf48f7e8c7f1df42b86136f (diff) | |
parent | 00e299467c9d5c7b44724e45079bb0fdf7e5654b (diff) | |
download | freeipa-48e70a4bf03e18c305d4a29fd0e0f83b8596c36b.tar.gz freeipa-48e70a4bf03e18c305d4a29fd0e0f83b8596c36b.tar.xz freeipa-48e70a4bf03e18c305d4a29fd0e0f83b8596c36b.zip |
Merge with upstream
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | ipa-admintools/Makefile | 2 | ||||
-rw-r--r-- | ipa-client/Makefile | 2 | ||||
-rw-r--r-- | ipa-python/Makefile | 8 | ||||
-rw-r--r-- | ipa-python/test/test_ipautil.py | 9 | ||||
-rw-r--r-- | ipa-server/Makefile | 2 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/controllers.py | 89 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/forms/user.py | 1 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/static/css/style.css | 2 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/static/javascript/effects.js | 1094 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/static/javascript/scriptaculous.js | 58 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/master.kid | 1 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usereditform.kid | 71 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usernewform.kid | 104 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/templates/usershow.kid | 40 | ||||
-rw-r--r-- | ipa-server/ipaserver/ipaldap.py | 8 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/funcs.py | 2 |
17 files changed, 1418 insertions, 80 deletions
@@ -60,6 +60,11 @@ clean: done rm -f *~ +test: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + version-update: sed s/VERSION/$(SERV_VERSION)/ ipa-server/freeipa-server.spec.in \ > ipa-server/freeipa-server.spec diff --git a/ipa-admintools/Makefile b/ipa-admintools/Makefile index 69bf18537..4bed3b9a6 100644 --- a/ipa-admintools/Makefile +++ b/ipa-admintools/Makefile @@ -10,3 +10,5 @@ install: clean: rm -f *~ *.pyc + +test: diff --git a/ipa-client/Makefile b/ipa-client/Makefile index a00d30bb4..d16e53f9f 100644 --- a/ipa-client/Makefile +++ b/ipa-client/Makefile @@ -21,3 +21,5 @@ clean: done rm -f *~ rm -f ipaclient/*~ + +test: diff --git a/ipa-python/Makefile b/ipa-python/Makefile index 542c50820..4b20d4bb7 100644 --- a/ipa-python/Makefile +++ b/ipa-python/Makefile @@ -1,6 +1,7 @@ PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib()") PACKAGEDIR ?= $(DESTDIR)/$(PYTHONLIBDIR)/ipa CONFIGDIR ?= $(DESTDIR)/etc/ipa +TESTS = $(wildcard test/*.py) all: ; @@ -15,3 +16,10 @@ install: clean: rm -f *~ *.pyc +.PHONY: test +test: $(subst .py,.tst,$(TESTS)) + +%.tst: %.py + python $< + + diff --git a/ipa-python/test/test_ipautil.py b/ipa-python/test/test_ipautil.py index 16991eff8..54ff1dc26 100644 --- a/ipa-python/test/test_ipautil.py +++ b/ipa-python/test/test_ipautil.py @@ -17,12 +17,17 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +import sys +sys.path.insert(0, ".") + import unittest -import ipa.ipautil + +import ipautil + class TestCIDict(unittest.TestCase): def setUp(self): - self.cidict = ipa.ipautil.CIDict() + self.cidict = ipautil.CIDict() self.cidict["Key1"] = "val1" self.cidict["key2"] = "val2" self.cidict["KEY3"] = "VAL3" diff --git a/ipa-server/Makefile b/ipa-server/Makefile index faee6e61c..8281a552e 100644 --- a/ipa-server/Makefile +++ b/ipa-server/Makefile @@ -21,3 +21,5 @@ clean: done rm -f *~ rm -f ipaserver/*~ + +test: diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index d767f2686..5fb4be06b 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -28,6 +28,8 @@ password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz client = ipa.ipaclient.IPAClient(True) client.set_principal("test@FREEIPA.ORG") +user_fields = ['*', 'nsAccountLock'] + def restrict_post(): if cherrypy.request.method != "POST": turbogears.flash("This method only accepts posts") @@ -77,6 +79,8 @@ class Root(controllers.RootController): new_user.setValue('sn', kw.get('sn')) new_user.setValue('mail', kw.get('mail')) new_user.setValue('telephonenumber', kw.get('telephonenumber')) + if kw.get('nsAccountLock'): + new_user.setValue('nsAccountLock', 'true') rv = client.add_user(new_user) turbogears.flash("%s added!" % kw['uid']) @@ -92,7 +96,7 @@ class Root(controllers.RootController): if tg_errors: turbogears.flash("There was a problem with the form!") - user = client.get_user_by_uid(uid) + user = client.get_user_by_uid(uid, user_fields) user_dict = user.toDict() # store a copy of the original user for the update later user_data = b64encode(dumps(user_dict)) @@ -120,6 +124,11 @@ class Root(controllers.RootController): new_user.setValue('sn', kw.get('sn')) new_user.setValue('mail', kw.get('mail')) new_user.setValue('telephonenumber', kw.get('telephonenumber')) + if kw.get('nsAccountLock'): + new_user.setValue('nsAccountLock', 'true') + else: + new_user.setValue('nsAccountLock', None) + # # this is a hack until we decide on the policy for names/cn/sn/givenName # @@ -161,7 +170,7 @@ class Root(controllers.RootController): def usershow(self, uid): """Retrieve a single user for display""" try: - user = client.get_user_by_uid(uid) + user = client.get_user_by_uid(uid, user_fields) return dict(user=user.toDict(), fields=forms.user.UserFields()) except ipaerror.IPAError, e: turbogears.flash("User show failed: " + str(e)) @@ -189,6 +198,82 @@ class Root(controllers.RootController): return password + @expose() + def suggest_uid(self, givenname, sn): + if (len(givenname) == 0) or (len(sn) == 0): + return "" + + uid = givenname[0] + sn[:7] + try: + client.get_user_by_uid(uid) + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): + return uid + + uid = givenname[:7] + sn[0] + try: + client.get_user_by_uid(uid) + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): + return uid + + uid = (givenname + sn)[:8] + try: + client.get_user_by_uid(uid) + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): + return uid + + uid = sn[:8] + try: + client.get_user_by_uid(uid) + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): + return uid + + suffix = 2 + template = givenname[0] + sn[:7] + while suffix < 20: + uid = template[:8 - len(str(suffix))] + str(suffix) + try: + client.get_user_by_uid(uid) + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): + return uid + suffix += 1 + + return "" + + @expose() + def suggest_email(self, givenname, sn): + if (len(givenname) == 0) or (len(sn) == 0): + return "" + + # TODO - get from config + domain = "freeipa.org" + + return "%s.%s@%s" % (givenname, sn, domain) + + + # TODO - mail is currently not indexed nor searchable. + # implement when it's done + # email = givenname + "." + sn + domain + # users = client.find_users(email, ['mail']) + # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0: + # return email + + # email = self.suggest_uid(givenname, sn) + domain + # users = client.find_users(email, ['mail']) + # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0: + # return email + + # suffix = 2 + # template = givenname + "." + sn + # while suffix < 20: + # email = template + str(suffix) + domain + # users = client.find_users(email, ['mail']) + # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0: + # return email + # suffix += 1 + + # return "" + + ######### # Group # diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py index 9ce512567..b9b6f33d4 100644 --- a/ipa-server/ipa-gui/ipagui/forms/user.py +++ b/ipa-server/ipa-gui/ipagui/forms/user.py @@ -10,6 +10,7 @@ class UserFields(): sn = widgets.TextField(name="sn", label="Last name") mail = widgets.TextField(name="mail", label="E-mail address") telephonenumber = widgets.TextField(name="telephonenumber", label="Phone") + nsAccountLock = widgets.CheckBox(name="nsAccountLock", label="Account Deactivated") uid.validator = validators.PlainText(not_empty=True) userpassword.validator = validators.String(not_empty=True) diff --git a/ipa-server/ipa-gui/ipagui/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css index 3c260c21b..9ea86ae01 100644 --- a/ipa-server/ipa-gui/ipagui/static/css/style.css +++ b/ipa-server/ipa-gui/ipagui/static/css/style.css @@ -122,7 +122,7 @@ body { } .formtable th { - width: 15%; + width: 20%; text-align: right; } diff --git a/ipa-server/ipa-gui/ipagui/static/javascript/effects.js b/ipa-server/ipa-gui/ipagui/static/javascript/effects.js new file mode 100644 index 000000000..70d07526c --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/static/javascript/effects.js @@ -0,0 +1,1094 @@ +// script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if(this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +} + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +} + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + return element; +} + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +} + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +Array.prototype.call = function() { + var args = arguments; + this.each(function(f){ f.apply(this, args) }); +} + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + tagifyText: function(element) { + if(typeof Builder == 'undefined') + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); + + var tagifyStyle = 'position:relative'; + if(Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + var options = Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, arguments[2] || {}); + Effect[element.visible() ? + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + return (pos > 1 ? 1 : pos); + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + }, + pulse: function(pos, pulses) { + pulses = pulses || 5; + return ( + Math.round((pos % (1/pulses)) * pulses) == 0 ? + ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : + 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) + ); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } +}; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(); +Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = (typeof effect.options.queue == 'string') ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i<len;i++) + this.effects[i] && this.effects[i].loop(timePos); + } +}); + +Effect.Queues = { + instances: $H(), + get: function(queueName) { + if(typeof queueName != 'string') return queueName; + + if(!this.instances[queueName]) + this.instances[queueName] = new Effect.ScopedQueue(); + + return this.instances[queueName]; + } +} +Effect.Queue = Effect.Queues.get('global'); + +Effect.DefaultOptions = { + transition: Effect.Transitions.sinoidal, + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' +} + +Effect.Base = function() {}; +Effect.Base.prototype = { + position: null, + start: function(options) { + function codeForEvent(options,eventName){ + return ( + (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + + (options[eventName] ? 'this.options.'+eventName+'(this);' : '') + ); + } + if(options.transition === false) options.transition = Effect.Transitions.linear; + this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn+(this.options.duration*1000); + this.fromToDelta = this.options.to-this.options.from; + this.totalTime = this.finishOn-this.startOn; + this.totalFrames = this.options.fps*this.options.duration; + + eval('this.render = function(pos){ '+ + 'if(this.state=="idle"){this.state="running";'+ + codeForEvent(options,'beforeSetup')+ + (this.setup ? 'this.setup();':'')+ + codeForEvent(options,'afterSetup')+ + '};if(this.state=="running"){'+ + 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ + 'this.position=pos;'+ + codeForEvent(options,'beforeUpdate')+ + (this.update ? 'this.update(pos);':'')+ + codeForEvent(options,'afterUpdate')+ + '}}'); + + this.event('beforeStart'); + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).add(this); + }, + loop: function(timePos) { + if(timePos >= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = Math.round(pos * this.totalFrames); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if(typeof this[property] != 'function') data[property] = this[property]; + return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Event = Class.create(); +Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { + initialize: function() { + var options = Object.extend({ + duration: 0 + }, arguments[0] || {}); + this.start(options); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(); +Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if(this.options.mode == 'absolute') { + // absolute movement, so we need to calc deltaX and deltaY + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: Math.round(this.options.x * position + this.originalLeft) + 'px', + top: Math.round(this.options.y * position + this.originalTop) + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); +}; + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%','pt'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if(/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = {}; + if(this.options.scaleX) d.width = Math.round(width) + 'px'; + if(this.options.scaleY) d.height = Math.round(height) + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if(this.options.scaleY) d.top = -topd + 'px'; + if(this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if(this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = {}; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if(!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if(!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + if(this.options.offset) offsets[1] += this.options.offset; + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if(effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element) + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {})); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }) + } + }, arguments[1] || {})); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); + effect.element.down().undoPositioned(); + } + }, arguments[1] || {}) + ); +} + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = element.getInlineOpacity(); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || {})); +}; + +Effect.Morph = Class.create(); +Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + if(!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: {} + }, arguments[1] || {}); + if (typeof options.style == 'string') { + if(options.style.indexOf(':') == -1) { + var cssText = '', selector = '.' + options.style; + $A(document.styleSheets).reverse().each(function(styleSheet) { + if (styleSheet.cssRules) cssRules = styleSheet.cssRules; + else if (styleSheet.rules) cssRules = styleSheet.rules; + $A(cssRules).reverse().each(function(rule) { + if (selector == rule.selectorText) { + cssText = rule.style.cssText; + throw $break; + } + }); + if (cssText) throw $break; + }); + this.style = cssText.parseStyle(); + options.afterFinishInternal = function(effect){ + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + if(transform.style != 'opacity') + effect.element.style[transform.style] = ''; + }); + } + } else this.style = options.style.parseStyle(); + } else this.style = $H(options.style) + this.start(options); + }, + setup: function(){ + function parseColor(color){ + if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ) + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if(value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if(property == 'opacity') { + value = parseFloat(value); + if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if(Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ) + }); + }, + update: function(position) { + var style = {}, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + transform.originalValue + Math.round( + ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create(); +Object.extend(Effect.Transform.prototype, { + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || {}; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + var data = $H(track).values().first(); + this.tracks.push($H({ + ids: $H(track).keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var elements = [$(track.ids) || $$(track.ids)].flatten(); + return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.prototype.parseStyle = function(){ + var element = document.createElement('div'); + element.innerHTML = '<div style="' + this + '"></div>'; + var style = element.childNodes[0].style, styleRules = $H(); + + Element.CSS_PROPERTIES.each(function(property){ + if(style[property]) styleRules[property] = style[property]; + }); + if(Prototype.Browser.IE && this.indexOf('opacity') > -1) { + styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; + } + return styleRules; +}; + +Element.morph = function(element, style) { + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); + return element; +}; + +['getInlineOpacity','forceRerendering','setContentZoom', + 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( + function(f) { Element.Methods[f] = Element[f]; } +); + +Element.Methods.visualEffect = function(element, effect, options) { + s = effect.dasherize().camelize(); + effect_class = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[effect_class](element, options); + return $(element); +}; + +Element.addMethods();
\ No newline at end of file diff --git a/ipa-server/ipa-gui/ipagui/static/javascript/scriptaculous.js b/ipa-server/ipa-gui/ipagui/static/javascript/scriptaculous.js new file mode 100644 index 000000000..7c472a600 --- /dev/null +++ b/ipa-server/ipa-gui/ipagui/static/javascript/scriptaculous.js @@ -0,0 +1,58 @@ +// script.aculo.us scriptaculous.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.7.1_beta3', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write('<script type="text/javascript" src="'+libraryName+'"></script>'); + }, + REQUIRED_PROTOTYPE: '1.5.1', + load: function() { + function convertVersionString(versionString){ + var r = versionString.split('.'); + return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); + } + + if((typeof Prototype=='undefined') || + (typeof Element == 'undefined') || + (typeof Element.Methods=='undefined') || + (convertVersionString(Prototype.Version) < + convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) + throw("script.aculo.us requires the Prototype JavaScript framework >= " + + Scriptaculous.REQUIRED_PROTOTYPE); + + $A(document.getElementsByTagName("script")).findAll( function(s) { + return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) + }).each( function(s) { + var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); + var includes = s.src.match(/\?.*load=([a-z,]*)/); + (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( + function(include) { Scriptaculous.require(path+include+'.js') }); + }); + } +} + +Scriptaculous.load();
\ No newline at end of file diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid index 8e8d26551..2f39afc41 100644 --- a/ipa-server/ipa-gui/ipagui/templates/master.kid +++ b/ipa-server/ipa-gui/ipagui/templates/master.kid @@ -10,6 +10,7 @@ @import "${tg.url('/static/css/style.css')}"; </style> <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/prototype.js')}"></script> + <script type="text/javascript" charset="utf-8" src="${tg.url('/static/javascript/scriptaculous.js?load=effects')}"></script> </head> <body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()"> diff --git a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid index 771d94d49..dc61f38c5 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid @@ -7,6 +7,34 @@ py:replace="field.display(value_for(field), **params_for(field))" /> + <div class="formsection">Identity Details</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" for="${user.givenname.field_id}" + py:content="user.givenname.label" />: + </th> + <td> + <span py:replace="user.givenname.display(value_for(user.givenname))" /> + <span py:if="tg.errors.get('givenname')" class="fielderror" + py:content="tg.errors.get('givenname')" /> + + </td> + </tr> + + <tr> + <th> + <label class="fieldlabel" for="${user.sn.field_id}" + py:content="user.sn.label" />: + </th> + <td> + <span py:replace="user.sn.display(value_for(user.sn))" /> + <span py:if="tg.errors.get('sn')" class="fielderror" + py:content="tg.errors.get('sn')" /> + </td> + </tr> + </table> + <div class="formsection">Account Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> @@ -85,34 +113,6 @@ </tr> </table> - <div class="formsection">Identity Details</div> - <table class="formtable" cellpadding="2" cellspacing="0" border="0"> - <tr> - <th> - <label class="fieldlabel" for="${user.givenname.field_id}" - py:content="user.givenname.label" />: - </th> - <td> - <span py:replace="user.givenname.display(value_for(user.givenname))" /> - <span py:if="tg.errors.get('givenname')" class="fielderror" - py:content="tg.errors.get('givenname')" /> - - </td> - </tr> - - <tr> - <th> - <label class="fieldlabel" for="${user.sn.field_id}" - py:content="user.sn.label" />: - </th> - <td> - <span py:replace="user.sn.display(value_for(user.sn))" /> - <span py:if="tg.errors.get('sn')" class="fielderror" - py:content="tg.errors.get('sn')" /> - </td> - </tr> - </table> - <div class="formsection">Contact Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> @@ -139,6 +139,21 @@ </tr> </table> + <div class="formsection">Account Status</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" for="${user.nsAccountLock.field_id}" + py:content="user.nsAccountLock.label" />: + </th> + <td> + <span py:replace="user.nsAccountLock.display(value_for(user.nsAccountLock))" /> + <span py:if="tg.errors.get('nsAccountLock')" class="fielderror" + py:content="tg.errors.get('nsAccountLock')" /> + </td> + </tr> + </table> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> <th> diff --git a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid index df5fd55fc..d4863a3df 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usernewform.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usernewform.kid @@ -2,6 +2,67 @@ class="simpleroster"> <form action="${action}" name="${name}" method="${method}" class="tableform"> + <div class="formsection">Identity Details</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" for="${user.givenname.field_id}" + py:content="user.givenname.label" />: + </th> + <td> + <span py:replace="user.givenname.display(value_for(user.givenname))" /> + <span py:if="tg.errors.get('givenname')" class="fielderror" + py:content="tg.errors.get('givenname')" /> + + </td> + </tr> + + <tr> + <th> + <label class="fieldlabel" for="${user.sn.field_id}" + py:content="user.sn.label" />: + </th> + <td> + <span py:replace="user.sn.display(value_for(user.sn))" /> + <span py:if="tg.errors.get('sn')" class="fielderror" + py:content="tg.errors.get('sn')" /> + <script type="text/javascript"> + function autofill(self) { + givenname = document.getElementById('form_givenname'); + sn = document.getElementById('form_sn'); + if ((givenname.value == "") || (sn.value == "")) { + return; + } + uid = document.getElementById('form_uid'); + mail = document.getElementById('form_mail'); + if (uid.value == "") { + new Ajax.Request('${tg.url('/suggest_uid')}', { + method: 'get', + parameters: {'givenname': givenname.value, 'sn': sn.value}, + onSuccess: function(transport) { + uid.value = transport.responseText; + new Effect.Highlight(uid); + } + }); + } + if (mail.value == "") { + new Ajax.Request('${tg.url('/suggest_email')}', { + method: 'get', + parameters: {'givenname': givenname.value, 'sn': sn.value}, + onSuccess: function(transport) { + mail.value = transport.responseText; + new Effect.Highlight(mail); + } + }); + } + } + document.getElementById('form_givenname').onchange = autofill + document.getElementById('form_sn').onchange = autofill + </script> + </td> + </tr> + </table> + <div class="formsection">Account Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> @@ -63,34 +124,6 @@ </tr> </table> - <div class="formsection">Identity Details</div> - <table class="formtable" cellpadding="2" cellspacing="0" border="0"> - <tr> - <th> - <label class="fieldlabel" for="${user.givenname.field_id}" - py:content="user.givenname.label" />: - </th> - <td> - <span py:replace="user.givenname.display(value_for(user.givenname))" /> - <span py:if="tg.errors.get('givenname')" class="fielderror" - py:content="tg.errors.get('givenname')" /> - - </td> - </tr> - - <tr> - <th> - <label class="fieldlabel" for="${user.sn.field_id}" - py:content="user.sn.label" />: - </th> - <td> - <span py:replace="user.sn.display(value_for(user.sn))" /> - <span py:if="tg.errors.get('sn')" class="fielderror" - py:content="tg.errors.get('sn')" /> - </td> - </tr> - </table> - <div class="formsection">Contact Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> @@ -117,6 +150,21 @@ </tr> </table> + <div class="formsection">Account Status</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" for="${user.nsAccountLock.field_id}" + py:content="user.nsAccountLock.label" />: + </th> + <td> + <span py:replace="user.nsAccountLock.display(value_for(user.nsAccountLock))" /> + <span py:if="tg.errors.get('nsAccountLock')" class="fielderror" + py:content="tg.errors.get('nsAccountLock')" /> + </td> + </tr> + </table> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> <th> diff --git a/ipa-server/ipa-gui/ipagui/templates/usershow.kid b/ipa-server/ipa-gui/ipagui/templates/usershow.kid index c21ff12bb..4e73eba35 100644 --- a/ipa-server/ipa-gui/ipagui/templates/usershow.kid +++ b/ipa-server/ipa-gui/ipagui/templates/usershow.kid @@ -8,41 +8,41 @@ <body> <h2>View Person</h2> - <div class="formsection">Account Details</div> + <div class="formsection">Identity Details</div> <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> <th> - <label class="fieldlabel" py:content="fields.uid.label" />: + <label class="fieldlabel" py:content="fields.givenname.label" />: </th> - <td>${user.get("uid")}</td> + <td>${user.get("givenname")}</td> </tr> <tr> <th> - <label class="fieldlabel" py:content="fields.uidnumber.label" />: + <label class="fieldlabel" py:content="fields.sn.label" />: </th> - <td>${user.get("uidnumber")}</td> + <td>${user.get("sn")}</td> </tr> + </table> + + <div class="formsection">Account Details</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> <th> - <label class="fieldlabel" py:content="fields.gidnumber.label" />: + <label class="fieldlabel" py:content="fields.uid.label" />: </th> - <td>${user.get("gidnumber")}</td> + <td>${user.get("uid")}</td> </tr> - </table> - - <div class="formsection">Identity Details</div> - <table class="formtable" cellpadding="2" cellspacing="0" border="0"> <tr> <th> - <label class="fieldlabel" py:content="fields.givenname.label" />: + <label class="fieldlabel" py:content="fields.uidnumber.label" />: </th> - <td>${user.get("givenname")}</td> + <td>${user.get("uidnumber")}</td> </tr> <tr> <th> - <label class="fieldlabel" py:content="fields.sn.label" />: + <label class="fieldlabel" py:content="fields.gidnumber.label" />: </th> - <td>${user.get("sn")}</td> + <td>${user.get("gidnumber")}</td> </tr> </table> @@ -62,6 +62,16 @@ </tr> </table> + <div class="formsection">Account Status</div> + <table class="formtable" cellpadding="2" cellspacing="0" border="0"> + <tr> + <th> + <label class="fieldlabel" py:content="fields.nsAccountLock.label" />: + </th> + <td>${user.get("nsAccountLock")}</td> + </tr> + </table> + <a href="${tg.url('/useredit', uid=user.get('uid'))}">edit</a> </body> diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index 4d0630eef..164509263 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -29,6 +29,7 @@ import urllib2 import socket import ldif import re +import string import ldap import cStringIO import time @@ -382,8 +383,11 @@ class IPAdmin(SimpleLDAPObject): REPLACE operations, to deal with multi-user updates more properly.""" modlist = [] - keys = set(old_entry.keys()) - keys.update(new_entry.keys()) + old_entry = ipautil.CIDict(old_entry) + new_entry = ipautil.CIDict(new_entry) + + keys = set(map(string.lower, old_entry.keys())) + keys.update(map(string.lower, new_entry.keys())) for key in keys: new_values = new_entry.get(key, []) diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index 8601bbf8a..e3e58ce94 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -270,8 +270,6 @@ class IPAServer: if user.get('gn'): del user['gn'] - if user.get('givenname'): - del user['givenname'] # some required objectclasses entry.setValues('objectClass', 'top', 'posixAccount', 'shadowAccount', 'account', 'person', 'inetOrgPerson', 'organizationalPerson', 'krbPrincipalAux', 'krbTicketPolicyAux') |