How to save variables after quitting and reopening in AppleScript? - variables

I am writing a very complicated AppleScript application that requires variables to be saved after quitting. So if I set the variable while it is running, close it, reopen it the variable will still be the same.
This script is intended to bring up a setup menu on first run. Then save the preferences for after the application is closed. More Technical explanation:
When it starts (on run) it will check if isSetup is false if it is it will go to the function setup(). The setup() function sets preferences and sets isSetup to true. When I quit and reopen the application is runs the setup() function again.
I know I am not supposed to copy and paste full scripts but I can't find the replicate the error without it. Here it is:
--AppleScript: menu bar script -- Created 2017-03-03 by Takaaki Naganoya adapted by ----
--2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
--http://piyocast.com/as/archives/4502
property aStatusItem : missing value
global theToggle
global theMenuTitle
global aTag
global aTitle
global isSetup
global usrName
global usrPass
global usrProtocol
property usrName : missing value
property usrPass : missing value
property isSetup : false
property usrProtocol : missing value
on run
if isSetup is false then
setup()
else
set theToggle to "Connect"
set theMenuTitle to "Server Helper"
init()
end if
end run
on init()
set aList to {theToggle, "Preferences", "Change Password", "", "Quit"}
set aStatusItem to current application's NSStatusBar's systemStatusBar()'s statusItemWithLength:(current application's NSVariableStatusItemLength)
aStatusItem's setTitle:theMenuTitle
aStatusItem's setHighlightMode:true
aStatusItem's setMenu:(createMenu(aList) of me)
end init
on createMenu(aList)
set aMenu to current application's NSMenu's alloc()'s init()
set aCount to 1
repeat with i in aList
set j to contents of i
if j is not equal to "" then
set aMenuItem to (current application's NSMenuItem's alloc()'s initWithTitle:j action:"actionHandler:" keyEquivalent:"")
else
set aMenuItem to (current application's NSMenuItem's separatorItem())
end if
(aMenuItem's setTarget:me)
(aMenuItem's setTag:aCount)
(aMenu's addItem:aMenuItem)
if j is not equal to "" then
set aCount to aCount + 1
end if
end repeat
return aMenu
end createMenu
on setup()
display dialog " Welcome to the Server setup Utility.
To Begin click " & quote & "Continue" & quote & " below." buttons {"Cancel", "Continue"} default button 2
set theButton to the button returned of the result
if theButton is "Continue" then
display dialog "Please enter your " & quote & "Username" & quote & " for the Brown Server." default answer "Username" buttons {"Continue"} default button 1
set usrName to the text returned of the result
display dialog "Please enter your " & quote & "Password" & quote & " for the Brown Server." default answer "" buttons {"Continue"} default button 1 with hidden answer
set usrPass to the text returned of the result
set listDeProtocols to {"AFP", "SMB", "WebDav", "FTP"}
set usrProtocol to (choose from list listDeProtocols with prompt "Choose Your Prefered Protocol. AFP is recomended. If AFP does not work try SMB. All others are not supported at this time")
set isSetup to true
postSet()
end if
end setup
on postSet()
if isSetup is false then
setup()
else
set theToggle to "Connect"
set theMenuTitle to "Server Helper"
init()
end if
end postSet
on changePref()
end changePref
on pref()
set length1 to the length of usrPass
set p1 to ""
set p2 to ""
repeat length1 times
set p1 to "•"
set p2 to p1 & p2
end repeat
display dialog "These are your following preferences. Click the " & quote & "Change" & quote & " to change.
Username: " & usrName & "
Password: " & p2 & "
Prefered Protocol: " & usrProtocol buttons {"Back", "Change"}
set theButton to the button returned of the result
if theButton is "Change" then
changePref()
end if
end pref
on actionHandler:sender
set aTag to tag of sender as integer
set aTitle to title of sender as string
if aTitle is not equal to "Quit" then
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
if aTitle is "Connect" then
set theToggle to "Disconnect"
init()
end if
if aTitle is "Disconnect" then
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
set theToggle to "Connect"
init()
end if
if aTitle is "Preferences" then
pref()
end if
if aTitle is "Change Password" then
changePass()
end if
else
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
end if
end actionHandler:

Update: doesn't work
YES!!!! I finally found the answer. You have to remove the property aStatusItem : missing value from the stop of the script. This will prevent aStatusItem from being used between functions. Because of this the menu bar won't be removed when you press quit. To fix that problem at the end change current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem to tell me to quit. This quits the application resulting in the menu bar item being removed.

Related

VBA Label Property is showing FALSE at the end of code

I am trying to update Label Caption property as a part of progress of code being executed to let user know what task is being performed. Everything works great until code is completed. In the end it is showing FALSE value instead of retailing all the message what was shown. How do I make sure it keeps all the message which was shown ? Forms!Main!Message.Caption = Forms!Main!Message.Caption is the one showing progress of message to the Label Control.
Here is sample code:
If strSource_System <> "" Then
'--Download AP Data
Forms!Main!.ctrlProgressBar.Value = Timer - startTime
Forms!Main!Message.Caption = Forms!Main!Message.Caption & vbCrLf & "Downloading AP Data for System " & strSource_System & ". Please be patient..."
SleepSec (1)
Forms!Main!.ctrlProgressBar.Value = Timer - startTime
Call Download_AP_Data(Source_CONNECTION, "SOURCE")
Forms!Main!.ctrlProgressBar.Value = Timer - startTime
'Reset Clock
startTime = Timer
Forms!Main!Message.Caption = Forms!Main!Message.Caption & vbCrLf & "AP Data Downloaded for System " & strSource_System & " Successfully."
SleepSec (1)
Forms!Main!.ctrlProgressBar.Value = Timer - startTime
End If
You are appending all messages to the same label. The label's text might exceed its maximum length at some point and produce an error. If you have an On Error Resume Next, you will not see this error.
I suggest using a ListBox instead. This allows you to add the messages as new items into the ListBox. The Row Source Type of the ListBox must be Value List.
A Label can hold a maximum of 2,048 characters. A ListBox in ValueList mode can hold up to 64K characters (i.e., 32 times more).
If this is not enough, bind the ListBox to a table and add up to 64 records (i.e., up to 64k messages).
But you could also limit the number of messages displayed and start deleting the first messages when exceeding this limit to get a rolling display.
messageListBox.AddItem "Downloading AP Data for System " & strSource_System & ". Please be patient..."
DoEvents
And if you automatically want to scroll to the last item added, you can do this
' Scroll to the last item
messageListBox.SetFocus 'Next line does not work otherwise.
messageListBox.ListIndex = messageListBox.ListCount - 1
' Unselect it
messageListBox.Selected(messageListBox.ListCount - 1) = False
It's easier to extract this code to another Sub
Private Sub DisplayMessage(ByVal message As String)
messageListBox.AddItem message
messageListBox.SetFocus
messageListBox.ListIndex = messageListBox.ListCount - 1
messageListBox.Selected(messageListBox.ListCount - 1) = False
DoEvents
End Sub
and then call it with
DisplayMessage "some message"
If you make this Sub public, then you can even place it in the Main form and then call it from the module with
Forms!Main.DisplayMessage "some message"
This has the advantage that this module does not have to know anything about a label or ListBox. The same could be done with a Sub ShowProgress.

Monitoring Logged In Users in MS Access 2016

I have a form on a tab that I use to monitor users who are logged in to my application.
The table is called USysRollCall (prefixed with USys to automatically make it invisible as a system table if access is gained to the navigation pane)
Only 3 fields are necessary: UserName (string) , As_of (date) and Logged_In (boolean)
All new users are automatically added to the UsysRollCall table
A "frm_Invisible" form launches with my main form and remains in the background. On the load and unload event of this form, I call the following routine like this:
Upon logging in: UserActive(AcquireUser, True)
Upon Logging Out UserActive(AcquireUser, False)
Public Sub UserActive(strUserName As String, bTrueFalse)
If bTrueFalse = True Then
CurrentDb.Execute "UPDATE USysRollCall SET Logged_In =-1, As_of=#" & Now & "# WHERE UserName='" & strUserName & "';"
Else
CurrentDb.Execute "UPDATE USysRollCall SET Logged_In =0, As_of=#" & Now & "# WHERE UserName='" & strUserName & "';"
End If
End Sub
'The following function can be used any time to get the username. It also serves a dual purpose of automatically taking necessary actions to add a new username to the user table if it is called and a username is not in the table. (not provided for this example)
Public Function AcquireUser() As String
'Returns the username
'but checks to see if it is in the table - if not... it adds it to the table as the lowest security level
Dim strUserName As String
strUserName = GetUserName
If Len(CheckUser(strUserName)) = 0 Then
MsgBox "Will be adding " & strUserName & " to the database"
AddUser (strUserName)
AcquireUser = strUserName
'MsgBox GreetUser(strUSERNAME), vbInformation, "Welcome"
Else
AcquireUser = strUserName
End If
End Function
I can verify that when I login and check the backend table, UsysRollCall I see a check in the Logged_In box. I can also verify that no matter how I close the database i.e. appropriately by using my quit button - or even just using the close X in the top right, that it still effectively logs me out.
I cannot verify what causes people to remain "Logged_In"
Is there a piece I am missing?

How to suppress an OS X error dialog in applescript or how to make sure a remote share is connected and sharing before using a mount volume command

Okay so I've been a few days on this, and I've only found one similar issue. Unfortunately it wasn't resolved fully.
I have a script that checks for a network connection, checks for the remote computer, and mounts the volumes.
The script works as expected and is error handled with a try blocks but in the mountVolume() handler I get the same error dialog box that Daniel from the other post gets when the share is unavailable. e.g. external drive is unplugged from remote computer or hasn't finished connecting yet.
Once I dismiss OS X's error dialog(s), my error dialog comes up. I could just get rid of my dialog but the thing is that for every share (4 now) I've tried to mount that fails, I get a separate OS X error dialog that I have to dismiss.
If I can get the script to suppress these boxes I can use one box of my own in the script for all errors.
If I can't then I would like a way to check if the share exists on the REMOTE computer before I try to use mount volume thus circumnavigating the error all together.
Thanks any ideas would be appreciated.
Here is my code:
global userName
global userPass
global ipAddress
set ipAddress to "###.###.###.###"
set userName to short user name of (system info)
set userPass to do shell script ("security find-internet-password -a " & userName & " -s " & ipAddress & " -g")
on FileExists(theFile)
tell application "System Events"
if exists file theFile then
return true
else
return false
end if
end tell
end FileExists
on FolderExists(theFolder)
tell application "System Events"
if exists folder theFolder then
return true
else
return false
end if
end tell
end FolderExists
on doCleanUp()
if FolderExists("/Volumes/SHARENAME") then
tell application "Finder" to eject disk "SHARENAME"
end if
set checkPath to ((path to home folder as text) & "SHARENAME")
if FileExists(checkPath) then
do shell script ("rm ~/SHARENAME")
end if
end doCleanUp
on checkNet()
try
do shell script ("nc -z " & ipAddress & " 445")
return true
on error
return false
end try
end checkNet
on mountVolume()
try
mount volume "smb://" & ipAddress & "/SHARENAME"
return true
on error errText number errNum
log {errText, errNum}
return false
end try
end mountVolume
on makeAlias()
if FolderExists("/Volumes/SHARENAME") then
set checkPath to ((path to home folder as text) & "SHARENAME")
tell application "Finder"
if not (exists file checkPath) then
make new alias to disk "SHARENAME" at path to home folder
end if
end tell
end if
end makeAlias
set tryAgain to 0
set ipValid to false
set doRetry to true
doCleanUp()
repeat while doRetry
repeat 3 times
if not ipValid then
set ipValid to checkNet()
end if
end repeat
if ipValid then
set volMounted to mountVolume()
if volMounted then
set aliasCreated to makeAlias()
if aliasCreated then
return
else
set notificationMessage to "Could not create alias."
display alert "An error has occurred." message notificationMessage as critical
return
end if
else
set notificationMessage to "Could not mount remote volume."
display alert "An error has occurred." message notificationMessage as critical
return
end if
else
set retryCheck to display alert "Can't connect. Do you want to retry?" buttons {"Yes", "No"} default button 1
set doRetry to button returned of retryCheck as boolean
if not doRetry then
doCleanUp()
set notificationMessage to "Could not connect to Local Area Network."
display alert "An error has occurred." message notificationMessage as critical
end if
end if
end repeat
The error dialog is being generated outside of AppleScript, so you can’t trap it with a try statement. The only way I know of to avoid the dialog is to create the mount point and mount the volume yourself with the mount shell script instead of the mount volume command, for example:
do shell script "mount -t smbfs //169.254.0.0/SHARENAME /path/to/sharepoint“
If there is an error the try statement will still catch it, just without the external dialog.

How to reference the users current textbox?

In access I am trying to not let the user exit a textbox unless they have filled it out (i.e. not left it null). This is to be done in a function in order to cut down on code. In VBA is there some way to stop the user from exiting the function? (I know about setting the focus but I need the code to work from a multitude of different textboxes)
For reference my current code is as follows;
Function Reload()
If IsNull(EmployeeID.Value) Or IsNull([First Name].Value) Or IsNull([Surname].Value) Or IsNull(DOB.Value) Or IsNull(Position.Value) Or IsNull(Position.Value) Or IsNull(Mobile.Value) Or IsNull(Email.Value) Or IsNull(Address.Value) Or IsNull(Suburb.Value) Or IsNull(Postcode.Value) Or IsNull([Start Date].Value) Or IsNull(UserLogin.Value) Or IsNull(UserPassword.Value) Then
MsgBox "Please fill out all fields"
Else
DoCmd.RunCommand acCmdSaveRecord
Form.Refresh
End If
End Function
Thanks
The best technique I know is to use the BeforeUpdate event to run data validation. Using your if statements if the data is not valid then set
Cancel = True
and the data will not write. You would probably prefer to disable the default record navigation and form close buttons and use custom buttons so that you can trap the invalid data error and prevent moving to a new record or closing the form. But even if you allow the user to use the built in navigation buttons or close button Access will throw up error messages about being unable to save the current record and prompt the user if he wants to proceed.
You can also go a step further and captue which fields are invalid and present a message box.
Msgbox ("The following fields were left blank and must be entered to save this record:" & vbCrLf & "Field 1" & vbCrLf & "Field 2")
A little modification of the text input limiter i use in our chat room should help. New function I have called it MustINPUT and call it on the LostFocus of the text boxes that you don't want to move from with out input.
Private Sub MyTextBox_LostFocus()
Call MustINPUT (Me.MyTextBox, 0)
End Sub
Just change the message for your users.
Sub MustINPUT(ctl As Control, iMaxLen As Integer)
'Call MustINPUT(Me.txtSayThis, 0)
If Len(ctl.Text) = iMaxLen Then
MsgBox "Please Type something", vbExclamation, "Input Needed"
ctl.Text = Left(ctl.Text, iMaxLen)
ctl.SelStart = iMaxLen
End If
End Sub
Hope this help. DR ,

AppleScript: Main Script reading from random folder

How do you bundle an applescript that has a main script which is reading scripts randomly from a folder?
Check out this code: http://macosxautomation.com/applescript/linktrigger/index.html.
It's doing exactly that (besides other things) and you can use the sub-routine there for the script-handling.
Here is a full script that works when you follow these steps: save it as an application and put all your scripts into the "Resources" folder of that application (as described at the webpage, "Show Package Contents"). Good Luck.
property MyUserName : ""
if MyUserName is "" then
display dialog "User Name:" default answer "" buttons {"Cancel", "Continue…"} default button 2
copy the result as list to {returnedButton, returnedText}
if returnedButton is "Cancel" then return
-- What to do when the user did not input any text?
if returnedText is "" then return -- we stop.
set MyUserName to returnedText
say "Hello," & MyUserName & "!" using "Karen"
else
display dialog "User name: " & MyUserName buttons {"Ok"} default button 1 with icon 1 giving up after 10
end if
say "For your information, please start the Amuse App everytime you log on... and I will speak to you at random times during your visit with me." using "Karen"
delay 20
try
repeat 105 times
set rnd to (random number from 1 to 105)
set rndFileName to (rnd as text) & ".scpt"
if my run_scriptfile(rndFileName) is false then -- if the script execution fails,…
error number -128 -- … stop this app
end if
end repeat
on error the error_message number the error_number
display dialog "Error: " & the error_number & ". " & the error_message buttons {"OK"} default button 1
return
end try
on run_scriptfile(this_scriptfile)
-- This handler will execute a script file
-- located in the Resources folder of this applet
try
set the script_file to path to resource this_scriptfile
return (run script script_file)
on error
return false
end try
end run_scriptfile
This is how it looks with (only) 3 of the scripts you wanna randomly execute:
Your question is clear but here's an script that loads randomly a script from a folder, if the folder contains any.
set theFolder to choose folder as string
set theFiles to do shell script "ls " & quoted form of posix path of theFolder & " | grep '.scpt$' || true"
if theFiles = "" then return
set theFile to some item of (paragraphs of theFiles)
set randomScript to load script file (theFolder & theFile)