System Preferences Applescripting

Let me start by saying I’m very new to applescripting so I’d really like any ideas or anything to help me learn. I have a lot of system preferences that I want to edit, but I’m just starting with one to learn from there.

I have a script (a very very large one) that goes into the Accounts pane of the system preferences, authenticates as Administrator, and creates a new user. It’s very long, and I know it could be a lot shorter with a lot less code, but thats not really my concern for right now so much. I’m more just wanting to learn how to get to the next step…

In this script, I have an account that is created called FRStudent with the password frlab. Now I want to set the computer up to automatically log in to this account everytime the computer is booted.

I cannot figure out how to click the “Login Options” in that left pane in the Accounts preference pane. Or maybe there is a different way to get it set? If you could help me get into that login options, that would be really appreciated.

I’ll post all the code I have below, but all this code just gets me up to the point where I need to get into that Login Options to select the user.

activate application "Finder"

tell application "System Events"
	tell process "Finder"
		tell menu bar 1
			tell menu bar item "Apple"
				tell menu 1
					click menu item "System Preferences."
				end tell
			end tell
		end tell
	end tell
end tell

activate application "System Preferences"

tell application "System Events"
	tell process "System Preferences"
		application "System Preferences"
		tell menu bar 1
			tell menu bar item "View"
				tell menu 1
					click menu item "Accounts"
				end tell
			end tell
		end tell
	end tell
end tell

activate application "System Preferences"

tell application "System Events"
	tell process "System Preferences"
		application "System Preferences"
		click button "Click the lock to make changes." of window "Accounts"
	end tell
end tell

activate application "SecurityAgent"

tell application "System Events"
	tell process "SecurityAgent"
		tell window "Authenticate"
			tell group 1
				set value of text field 1 to "Administrator"
				set value of text field 2 to "myfakepassword"
			end tell
			tell group 2
				click button "OK"
			end tell
		end tell
	end tell
end tell

activate application "System Preferences"

tell application "System Events"
	tell process "System Preferences"
		application "System Preferences"
		click button "Button" of window "Accounts"		
	end tell
end tell

activate application "System Preferences"

tell application "System Events"
	tell process "System Preferences"
		application "System Preferences"
		tell sheet 1 of window "Accounts"
			set value of text field 1 to "FRStudent"
			set value of text field 2 to "frstudent"
			set value of text field 3 to "frlab"
			set value of text field 4 to "frlab"
			click button "Create Account"
		end tell
	end tell
end tell

Alright, I have everything now except for one last thing. Each part of this script now works all the way to selecting the user to log in automatically with and typing in the password. Theres one last thing that I hope somebody can help me out with. I kept testing the script from one part to the next, so I didn’t catch this error until I ran it from the beginning.

What I did to get to the Login Options button was I did this

activate application "System Preferences"

tell application "System Events"
	tell process "System Preferences"
		application "System Preferences"
		
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
		delay 0.3
		keystroke (ASCII character of 31)
	end tell
end tell

I just added a bunch of down arrows to make sure it would go down the list no matter how long it is. It all worked and went on to the next steps until I ran it from the beginning.
When you open System Preferences, the Search field in the top right of the window is highlighted, all keystrokes go in that box. So now my downarrows do nothing. Is there a way I can get that left side pane to be highligthed so the arrows will work there instead?

Thanks

you could probably do something like this:

activate application "System Preferences"

tell application "System Events"
   tell process "System Preferences"
       application "System Preferences"
       key down tab
       key up tab
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
       delay 0.3
       keystroke (ASCII character of 31)
   end tell
end tell

Basically, what I added was a tab down to the next field (which is the lis of users). From there, you can do your down arrow stuff and it should work.

hi jc,

i don’t like doing system level stuff in GUI scripting, so i took a look to see how i’d add users from the command line. i went ahead and worked up a script that works for me in 10.4 (ONLY tested in 10.4 Desktop) and the code is a lot shorter. i’m sure there is a way to specify the user for ‘Automatic Logon’ but i have not found that yet. (could it be a ‘defaults’ setting?). anyway, here’s the code, which you or somebody else might find useful:


-- createUser  creates a user using the niutil command line utility
--   and uses Expect to create the password

-- these next few lines are the contents of a file we will create to set the password
property myExpect : "#!/usr/bin/expect 
spawn passwd [lindex \\$argv 0]
set password [lindex \\$argv 1]
expect \"password:\"
send \"\\$password\\r\"
expect \"password:\"
send \"\\$password\\r\"
expect eof"

-- we get the highest user id in the 500 range, which we'll later increment and use for our new user
set getHighUID to (do shell script "/usr/bin/nireport / /users uid | /usr/bin/grep \"5[0-9][0-9]\"")
set howMany to number of words of getHighUID
set i to 1
set currWord to 0
set highWord to 0
repeat while i ≤ howMany
	set currWord to word i of getHighUID
	if currWord > highWord then
		set highWord to currWord
	end if
	set i to (i + 1)
end repeat

-- get the list of users so we can compare the new user and reject if they already exist
set getUsers to text of (do shell script "/usr/bin/nireport / /users name")

-- get the username and password (twice) as input from the user
set newUser to text returned of (display dialog "What is the name of the User you would like to add?" default answer "")
set userPass to text returned of (display dialog "What will be the password for this user?" default answer "" with hidden answer)
set userPassComp to text returned of (display dialog "Please reenter the password." default answer "" with hidden answer)

-- if the user already exists we need to error out.  no need to be fancy.
-- if not, we will create the user, home directory & contents, plus the password.
if userPass is not userPassComp then
	display dialog "Those two passwords don't match.  Please try again."
else
	if newUser is in getUsers then
		display dialog "That User already exists.  Please try again."
	else
		do shell script "/usr/bin/niutil -create / /users/" & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " realname " & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " uid " & (highWord + 1) with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " home /Users/" & newUser with administrator privileges
		do shell script "/usr/sbin/createhomedir -u " & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " shell \"/bin/bash\"" with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " gid 20" with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " passwd \"*\"" with administrator privileges
		do shell script "/bin/cat > /tmp/expectPass << EOF 
" & myExpect & "
EOF" with administrator privileges
		do shell script "/bin/chmod +x /tmp/expectPass" with administrator privileges
		do shell script "/tmp/expectPass " & newUser & space & userPass with administrator privileges
		display dialog "The new user " & newUser & " has been created."
		--if our Expect script exists, delete it.
		try
			do shell script "/bin/rm -rf /tmp/expectPass" with administrator privileges
		end try
	end if
end if

comments welcome.

EDITED: to add an if statement. then i changed it again. and again.

hello,

here is the completed script. as i suspected, there is a ‘defaults’ option to change the autoLoginUser:


-- createUser  creates a user using the niutil command line utility
--   and uses Expect to create the password

-- these next few lines are the contents of a file we will create to set the password
property myExpect : "#!/usr/bin/expect 
spawn passwd [lindex \\$argv 0]
set password [lindex \\$argv 1]
expect \"password:\"
send \"\\$password\\r\"
expect \"password:\"
send \"\\$password\\r\"
expect eof"

-- we get the highest user id in the 500 range, which we'll later increment and use for our new user
set getHighUID to (do shell script "/usr/bin/nireport / /users uid | /usr/bin/grep \"5[0-9][0-9]\"")
set howMany to number of words of getHighUID
set i to 1
set currWord to 0
set highWord to 0
repeat while i ≤ howMany
	set currWord to word i of getHighUID
	if currWord > highWord then
		set highWord to currWord
	end if
	set i to (i + 1)
end repeat

-- get the list of users so we can compare the new user and reject if they already exist
set getUsers to text of (do shell script "/usr/bin/nireport / /users name")

-- get the username and password (twice) as input from the user
set newUser to text returned of (display dialog "What is the name of the User you would like to add?" default answer "")
set userPass to text returned of (display dialog "What will be the password for this user?" default answer "" with hidden answer)
set userPassComp to text returned of (display dialog "Please reenter the password." default answer "" with hidden answer)

-- if the user already exists we need to error out.  no need to be fancy.
-- if not, we will create the user, home directory & contents, plus the password.
if userPass is not userPassComp then
	display dialog "Those two passwords don't match.  Please try again."
else
	if newUser is in getUsers then
		display dialog "That User already exists.  Please try again."
	else
		do shell script "/usr/bin/niutil -create / /users/" & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " realname " & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " uid " & (highWord + 1) with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " home /Users/" & newUser with administrator privileges
		do shell script "/usr/sbin/createhomedir -u " & newUser with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " shell \"/bin/bash\"" with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " gid 20" with administrator privileges
		do shell script "/usr/bin/niutil -createprop / /users/" & newUser & " passwd \"*\"" with administrator privileges
		do shell script "/bin/cat > /tmp/expectPass << EOF 
" & myExpect & "
EOF" with administrator privileges
		do shell script "/bin/chmod +x /tmp/expectPass" with administrator privileges
		do shell script "/tmp/expectPass " & newUser & space & userPass with administrator privileges
		set loginPref to button returned of (display dialog "The new user " & newUser & " has been created.  Would you like to configure this user as the AutoLogin default?" buttons {"Yes", "No"})
		if loginPref is "Yes" then
			do shell script "/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser " & quoted form of newUser with administrator privileges
		end if
		--if our Expect script exists, delete it.
		do shell script "/bin/rm -rf /tmp/expectPass" with administrator privileges
		display dialog "Finished"
	end if
end if

I’d not really been following this topic, but thought I’d take a quick look at your script, jc - just in case you’d prefer to stick with the UI scripting approach.

While the following version may not yet do all you want, it does everything you described in your initial notes. It should certainly take you beyond the impasse over accessing the Login Options - and without a keystroke in sight. In fact, it completes the creation of a new user account and quits System Preferences, so any additional tweaks to the settings should now be a pretty straightforward matter. Hope it helps…


to authenticate_changes()
	tell application "System Events" to tell window "Authenticate" of process "SecurityAgent"
		tell group 1
			set value of text field 1 to "Administrator"
			set value of text field 2 to "myfakepassword"
		end tell
		click button "OK" of group 2
	end tell
end authenticate_changes

tell application "System Preferences"
	reveal anchor "passwordPref" of pane "Accounts"
	activate
end tell

tell application "System Events" to tell window "Accounts" of process "System Preferences"
	tell button "Click the lock to make changes." to if exists then
		click
		my authenticate_changes()
	end if
	click (first button whose name is "Button" and help is "Add a user account")
	
	tell sheet 1
		set value of text field 1 to "FRStudent"
		set value of text field 2 to "frstudent"
		set value of text field 3 to "frlab"
		set value of text field 4 to "frlab"
		click button "Create Account"
		repeat while exists
			delay 0.3
		end repeat
	end tell
	
	tell application "System Preferences" to reveal anchor "loginOptionsPref" of pane "Accounts"
	
	tell group 1
		click checkbox "Automatically log in as:"
		tell pop up button 1
			click
			click (first menu item of menu 1 whose name is "FRStudent")
		end tell
	end tell
	
	tell sheet 1
		repeat until exists
			delay 0.2
		end repeat
		set value of text field 1 to "frlab"
		click button "OK"
	end tell
	click button "Click the lock to prevent further changes."
end tell

quit application "System Preferences"


hello kai and jc,

I have a similar issue. I need to script some changes to the second user in the Accounts list. Im a lab manager, and the admin account is first in the list with a managed account second on the list which the students use. I cant find a way to select or activate the second account in order to script changes to its settings. Ive tried everything I can think of from attempting to click by position to searching for a way to focus on “List 1” which is what that area is described as in Pre Fab UI browser (so I could use up and down arrow keystrokes to change which account Im on). I just cant find a way to select the second user. Any ideas?

Thanks in advance,

Rowie

Hi Rowie

This Works but please treat it like the blunt instrument it is. a few other factors could come into place
and usually do when UI scripting.
this selects the next user in the list

tell application "System Preferences"
	reveal anchor "passwordPref" of pane "Accounts"
	activate
end tell
tell application "System Events"
	tell process "System Preferences"
		tell window "Accounts"
			keystroke tab
			keystroke (ASCII character of 31)
		end tell
	end tell
end tell

Hi Pidge, thanks for the response but this does not work for me. The tab key only toggles between the search field at the top right and the fields within the various panes (password, parental control etc) but never sets the focus on the users list. I dont know if this is something system specific (Im using OS 10.4.8) but even without the scripting, just sitting here with my System Prefs open and attempting to get the focus on the users list, neither tab nor any other key seems to work. Ive tried it on other computers as well with the same result. Ive been fairly thorough in attempting to find a way to script this and have exhausted all ideas that involve any keystroke combinations or anything like that.

I was hoping someone might know an anchor name for the users list in the Accounts pane, or some other way to reference it, set the focus on it, or even just how to click it using x,y position. UI Browser generates the following code which, according to them, should click on the position of the icon of the second user, but it has no effect for me and I dont know why (Ive run tests to see if its clicking somewhere else, even in other programs such as a text edit doc where clicking somewhere would change the location of the cursor and the code appears to be ineffective) Of course, I used the appropriate Tell block to reference the Sys Prefs (or Text Edit during my test) before and after the code:

try
tell image 1 of group 2 of list 1 of window “Accounts”
set {xPosition, yPosition} to position
set {xSize, ySize} to size
end tell
– modify offsets if hot spot is not centered:
click at {xPosition + (xSize div 2), yPosition + (ySize div 2)}
end try

Any ideas why this doesnt work, or any other approaches to try? Many thanks…

Browser: Firefox 2.0.0.1
Operating System: Mac OS X (10.4)

Hi Rowie

Sorry this didn’t work for you. like i said blunt instrument.
It’s probably just the way are machines are set up i guess (i’m on 10.4.8 as well) and it works on mine
Is your padlock locked or unlocked this may make a difference it might be an idea to add the unlock bit
into this script…
if i get chance i’ll look further into this, i’m sure someone else with a bit more experience will no how
to solve this for you.
does this have a better result:

tell application "System Preferences"
	reveal anchor "passwordPref" of pane "Accounts"
	activate
end tell

tell application "System Events" to tell window "Accounts" of process "System Preferences"
	tell button "Click the lock to make changes." to if exists then
		click
		my authenticate_changes()
	end if
	delay 4
	keystroke tab
	keystroke (ASCII character of 31)
end tell

to authenticate_changes()
	tell application "System Events" to tell window "Authenticate" of process "SecurityAgent"
		tell group 1
			set value of text field 1 to "Administrator"
			set value of text field 2 to "yourpassword"
		end tell
		click button "OK" of group 2
	end tell
end authenticate_changes

good luck

Hmmm…interesting. Pidge, are you using an Intel machine or Power PC? I cant understand why it would work for you and not for me. The machines I have been using are all PPC running OS 10.4.8. I have the unlock portion of the code in my script already and have tried it manually with the lock unlocked, it makes no difference, I just cant use tab or any key combinations to focus on the users list. Even if I just manually click on the users list and hit tab, it will toggle to the search field and then to the fields within the pane, but never back to the users list.

What version of Sys Prefs are you running? Mine is 3.3

Thanks for the help.

-Rowie

Hi rowie

system prefs version 3.5
running on intel macbook pro!
maybe there lies the answer!
will test it on a power pc when i get chance…

sorry rowie just reading your last post again.
The tab for me just highlights the list of users the ascii keystroke 31 which is the down arrow skips to the second user
in the list.

Hi,

UI scripting is very sensitive with different localizations.
I tried to change the script to an language independent version.
On my Dual G5 with german 10.4.8 system this works:

tell application "System Preferences"
	reveal anchor "passwordPref" of pane id "com.apple.preferences.users"
	activate
end tell

tell application "System Events" to tell window 1 of process "System Preferences"
	tell button 4 to if exists then
		click
		my authenticate_changes()
	end if
	delay 4
	keystroke tab
	keystroke (ASCII character of 31)
end tell

to authenticate_changes()
	tell application "System Events" to tell window 1 of process "SecurityAgent"
		tell group 1
			set value of text field 1 to "Administrator"
			set value of text field 2 to "yourpassword"
		end tell
		click button "OK" of group 2
	end tell
end authenticate_changes

Thanks for the attempt Stefan, but this doesnt work. In this script, and the previously discussed ones, the tab key is being used to set the focus on the users list and then the down arrow to select the second user. The issue here appears to be that on the computers I am working with, the tab key does not activate or set the focus on the users list. Without even using Applecript, just manually attempting to use the tab key to set the focus on the users list in the Sys Prefs, it just doesnt work. The focus toggles between the search field at the top right and the fields within the various panes, but never sets the focus on the users list. I am using Power PC machines with OS 10.4.8 and System Preferences 3.3.

I thought maybe the difference in systems between myself and Pidge might have explained why it doesnt work for me, but Stefan, you said you are using a PPC mac with 10.4.8, so now Im really baffled. What version of Sys Prefs do you have Stefan?

Hi rowie,

sorry, I didn’t get the crucial point to select a second user.
I found out, that properly working of the script doesn’t depend on SysPref version (I have also 3.3)
but on the settings of “Full keyboard access” in Keyboard & Mouse > Keyboard Shortcuts.
If “All controls” are selected, tab key works

Ah, Stefan, you are truly my hero. I have to admit I feel a bit silly not ever having seen that option, but I guess I never had a use for it before. My sincerest thanks for bringing it to my attention…problem solved! :slight_smile:

Hi.

In kai’s original script, he used the following to click the ‘padlock’ button. (I’ve split the ‘tell’ lines here for clarity.):

tell application "System Events"
	tell window "Accounts" of process "System Preferences"
		tell button "Click the lock to make changes."
			if exists then
				click
				my authenticate_changes()
			end if
		end tell
		
		-- etc.
	end tell
end tell

Presumably the check to see if button “Click the lock to make changes.” exists is because when the padlock’s open, the button’s name changes to “Click the lock to prevent further changes.” The non-existence of a button called “Click the lock to make changes.” means that the padlock’s already open and the click and the authentication handler are to be skipped.

Stefan’s version of the script identifies the button by index number instead, presumably for greater compatibility with the buttons on his machine, which are named in German. The problem with this is that ‘button 4’ always exists, regardless of its name, so the click and the authentication are always performed. One possible way round this is to check the button’s size, since the ‘open padlock’ is slightly larger than the ‘closed padlock’. On my machine, the sizes are {190, 16} (closed) and {246, 15} (open).

tell application "System Events"
	tell window "Accounts" of process "System Preferences"
		tell button 4
			if (size is {190, 16}) then -- {190, 16} when padlock closed, {246, 16} when open.
				click
				my authenticate_changes()
			end if
		end tell
		
		-- etc.
	end tell
end tell

I think this would work for everyone, but I’m not totally sure. Any feedback would be appreciated.

Works for me Nigel.

Hey Guys
Merry Christmas!
Works fine here too!:smiley:

Thanks, guys. :slight_smile: