I need to have my Mac app move some files to the trash. In some cases, the user may not have access to these files without authenticating to root (ie, “sudo mv” would be required to move these files in the Terminal).
I know I could do this via plugin, but I don’t want to rely on any plugins. I also have read that it is just about impossible to do this “right” from Xojo. One good option I’ve found is to use an AppleScript to run a shell script “with administrator privileges”. This is directly supported by the system and will handle the authentication properly with a minimum of coding on my part. But, if I put a script in my project and call it, I get a password prompt each time, rather than just the first time.
I imagine I could solve this by including an AppleScript applet within my app that I could open, and which would stay open with a timeout delay after which it would quit, and that I could repeatedly ask to delete files. I know from experience that such an app will only ask for a password the first time you run a shell script with admin privileges. However, how would I make Xojo and that AppleScript applet communicate?
No, that’s a VERY bad way to do it. Authentication should never be done by asking the user for their user password directly. This should always be managed through the authentication features in Mac OS X, so that the user is never being asked to trust their password to the app.
Many users may not understand the difference, but security professionals do, and as a security guy, I would warn people away from an app that asked for the user’s password directly and passed it around in clear text. I cannot make my own app do something like that.
If you are calling the AppleScript from the shell, you may have a look at some of the links here :
The first one discusses several ways to pass variables to the script. I just tried successfully this :
on run this_file
tell application "TextWrangler"
open this_file
end tell
do shell script "echo This message is passed back to the shell."
end run
and called the script through :
osascript pass.scpt ~/Desktop/Letter.txt
I have added the shell script at the end, which passes back a message to the shell, so you can have it in shell.result. I guess all sorts of variables can be sent this way.
I am a bit concerned if you plan on having your app in the Mac App Store, though. Moving a file to the trash probably calls system events, and from what I know, it is an absolute no-no.
Not quite. I’m not calling AppleScript from the shell, I’m doing the opposite. I need the functionality of an AppleScript like this:
on run (aShellPath)
try
do shell script ("mv " & aShellPath & " ~/.Trash") with administrator privileges
end try
end run
I can save this as a script named (for example) MoveItemToTrash.scpt, then add that script file to my Xojo project and call it like this:
MoveItemToTrash(someFolderItem.ShellPath)
This works perfectly, and authenticates properly. The problem is, I may have multiple files that need to be moved with root-level permissions, and this will ask for the password each and every time. I want only one password prompt, as would be done normally if there were multiple calls using “with administrator privileges” in a single AppleScript. Each call to MoveItemToTrash is effectively a new instance of the script, and thus needs re-authentication every time.
I imagine I could do something similar by compiling the script into an AppleScript applet, then just keep that running as long as I need it. I just don’t know how to call into an AppleScript applet as if it were a function as Xojo can do with the script file, or if that’s even possible.
That’s not an issue. My app can never be in the App Store, because it needs to be able to remove files that an App Store app is not allowed to touch.
If I understand right, what would be needed to avoid identifying for each file would be to put all the calls in the script ?
I just did an experiment that may allow you to create the script that does all the multiple calls in one shot.
I saved the same scpt as before in text format. It runs flawlessly. What that means is that you can create your script from Xojo with all the calls you need in your script and then run it once. Just assemble it as a text file from the selected files path.
At least, I was able to call it from the Terminal, and it works fine with an osascript from the shell.
Yes, I could do it all in one run in the AppleScript… the only problem is, there’s a lot of work involved in finding each file within Xojo code, and most of the files don’t need special permissions to remove. I don’t want to blast all the files with admin permissions, because that would mean that my app would be basically useless for a non-admin user. I just want admin permissions for the ones that truly require it. But those are all mixed in with the others.
I could build a list of files that require admin permissions and remove them with a single call to an AppleScript, but then I’ll have to modify the entire flow of the file removal process (and screw up the progress window) in order to delete all those files at the end, out of order from all the other files being removed. It’s something I have considered, and will do if there’s not a better way, but it breaks the elegance of the code and the user interface in ways that I would rather avoid.
How do you list the files needed to move ? Why is it so difficult to find them ? Is it not possible to make a list of those which need permission, and those that don’t ?
The issue may be more to start with which hammer you got to push the nails, before the color of the toolbox. Maybe you want to leave the user interface aside for an instant, verify the feasibility of the engineering part, and when you got this working, optimize your code and beautify your UI …
I have wondered about this as well. Dealing with admin permissions is something the OS should take care of, not the app itself. That is what users (under OS X anyway) have come to expect. I would get suspicious if an app did try to get my admin password. Ideally this would be transparently handled by the Xojo runtime with the required help from the OS, as it can get quite difficult when the app developer has to handle these cases. Getting the required permissions should be transparently handled behind the scenes, with the developer only having to deal with the situation that some file operation failed because the required permissions were denied.
Does anybody know whether there is already a feature request to sign on?
on run (aShellPath)
try
do shell script ("mv " & aShellPath & " ~/.Trash") with administrator privileges
end try
end run
which runs a shell script which may not prompt tell the finder to move the specific item to the trash
that should
Or a declare to use the OS API to move the file to the trash - that too should prompt
Not being able to display a progress bar for what could be half of the files being moved - as would be the case if I have to delete a batch of them in AppleScript all at once - is a not insignificant user interface issue. Among other logistical issues.
In any case, though, the question here is not why I want to do this, but how to do it. I appreciate the advice, but ultimately, there are good reasons why I want to do it this way, and there has been nothing said so far that I haven’t already thought about. So, it would be great if we could confine the discussion to the hows rather than the whys.
My concern is that there simply will not be a “how” if I insist on developing this app with Xojo.
Have you looked at NSWorkspace recycleURLs:completionHandler:, you’ll need to the blocks plugin from Joe R or the MBS plugin to handle a block, but this is the recommend method for an application to move files to the Trash.
There is also NSFileManager trashItemAtURL:resultingItemURL:error: (which doesn’t require a plugin).
All you need to update a progressbar from an AppleScript is to be able to pass variables from the script to your app. Which you can do through shell script echo if you run the AppleScript with Osascript, or by [quote]tell application “yourapp”[/quote] and processing the event.
You brought up how difficult it was “there’s a lot of work involved in finding each file within Xojo”. It’s only natural, even polite, to be curious when a simple folderitem would do the job. Sorry if trying to understand to better assist offends you. Maybe showing interest was an error
[quote=126319:@Sam Rowlands]Have you looked at NSWorkspace recycleURLs:completionHandler:, you’ll need to the blocks plugin from Joe R or the MBS plugin to handle a block, but this is the recommend method for an application to move files to the Trash.
There is also NSFileManager trashItemAtURL:resultingItemURL:error: (which doesn’t require a plugin).[/quote]
Those techniques won’t remove files that the user doesn’t have access to without authentication through Mac OS X… which seems to be a bit of a problem in Xojo. Thus my attempt at a workaround using Xojo.
You misunderstand. It’s not a matter of creating a FolderItem. It’s a matter of hunting down these files that need to be removed. There’s a lot of code involved in locating them and identifying them as problem files. Which is part of the reason I’m using Xojo now… I’d written this as a simpler AppleScript applet, but the process of identifying the files had become a bigger problem than AppleScript was able to accomplish effectively.
Look, if you don’t have a clue how to do what I’m asking, or if you know that it’s not possible, then just say so. Otherwise, please spare the attitude. I’m just looking for an answer to a very specific question… which I’m starting to believe doesn’t exist.
I’m already exploring options for having an AppleScript remove all the files, and have been since before I started this topic. Your suggestion for how to pass progress information back from the AppleScript may work, but would only solve one one of the headaches I’m encountering with this method.
Some people have a strange compulsion to take on others their grudges. The only clue I have is that you are a very unpleasant individual who does not even have a hint of manners. Go on being a snit, if that makes you feel good I will not read your spew.
Wow. How childish. I ask that we focus on the question and not side issues, and suddenly I’m rude and throwing a snit. Whatever.
I’m wasting my time here. You don’t know the answer to my question, though you won’t admit it, and it seems that nobody else does either. This kind of thing - the need for arcane workarounds and a proliferation of people who pooh-poohed others’ desires for better - is what led me to leave the REALbasic community years ago. I’m sad that this still hasn’t changed.
There is a nicely put together “AuthorizationShell” class available through MacOSLib. Maybe it will help solve your problem (although it only works on Mac OS)?
Thanks for the suggestion. Unfortunately, that relies on OS calls that were deprecated in 10.7. I’d rather not become reliant on something like that, which will break in some future system at an unknown time in the future. I appreciate the pointer, though.
The main handler of your AppleScript is treated by Xojo just like a simple function call. Returning data from AppleScript is also simple: anything from the “return” statement will be returned to Xojo. There should be an example in the Xojo examples.
Remember that all data in and our are strings. But it’s not too complicated to convert between folder items and aliases in AppleScript. If you want to create dynamic AppleScripts WITH a plugin you can try my RunAS project at http://www.mothsoftware.com/content/xojo/ . I use this to manage dozens of AppleScripts in my app.