2

I have a small computer lab for students to use fairly unsupervised, with a printer attached on the network. I am trying to implement a simple scripting additions alert dialog with all the rules about the printer that I need to pop up when they select print from any number of different applications.

I am trying to attach the script directly to the printer itself in the User/Library/Printer directory, (xxx.xxx.xxx.xxx.app) so any browser, or pdf viewer, etc. will get the message displayed when they try to run the printer.

I have tried using automator with applescript, I have tried renaming the printer and calling the applescript the name of the printer, so far no good.

What am I missing?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
  • 1
    Please share what you tried so far. – Alex Mar 26 '18 at 23:31
  • My applescript right now is just a System Event call to display dialog with the message. I made that script a run-only application with the final line a command to open PRINTER.app. Then I renamed the printer in the user folder 'PRINTER.app' and named the run-only app what the printer name was. Running the script worked and triggered the Print queue, but the first print attempt from an app created a new printer and bypassed the script. – themightyrumpus Mar 27 '18 at 17:06
  • I also tried to create a way through automator to call the script, but it looks like the closest I can get is to create a Folder Action, which does not appear to be helpful at all in this situation. – themightyrumpus Mar 27 '18 at 17:07
  • 1
    You can not trigger an AppleScript when an .app runs or a printer is called. You can have a script launch an app, of course. You can have an always running script that provides the message when the print process activates, though that could be resource intense. – jweaks Mar 27 '18 at 20:10
  • Yeah, I'm trying to find some kind of out-of-the-box workaround right now. I wouldn't want a constant script running, but maybe a less conventional solution is possible. Maybe a script can be triggered when the screen saver is disabled? That way each time a new student sits down at an idle machine they are presented with the message? This one is tricky... – themightyrumpus Mar 28 '18 at 21:11
  • I can show you how to display an alert on application launch using JavaScript for Automation (JXA) if you modify your question to allow for that kind of answer, and add the `jxa` tag to the question. My approach is based on applying these answers, but using JXA's Objective-C bridge: https://stackoverflow.com/questions/550080/cocoa-application-finished-launch and https://stackoverflow.com/questions/1904690/how-to-programmatically-make-cocoa-application-active – bacongravy Mar 29 '18 at 02:22
  • Sounds like you just want somewhere to post the rules for the printer. Could you post them on the wall above the printer/computers, or change the desktop screensaver to include them? – Darrick Herwehe Mar 29 '18 at 13:46

1 Answers1

3

In this answer I will show how to create a JavaScript for Automation (JXA) applet that listens for app-launch and screensaver-stop notifications and then displays an alert when it receives one, thereby producing the desired outcome described in the question. I also describe how this approach can be adapted to trigger an AppleScript script, which would produce the specific behavior described in the title of the question.

Instructions

  1. Open the Script Editor app and create a new document
  2. From the pop-up near the top-left of the window, select JavaScript instead of AppleScript
  3. Paste in the code provided below
  4. Save the script as an applet by changing the 'File Format' to 'Application' in the save panel and enabling the 'Stay open after run handler' option.
  5. Run the applet by choosing 'Run Application' from the 'Script' menu
  6. Launch an app and notice an alert
  7. Start and then stop the screensaver and notice an alert

Code

var me = Application.currentApplication(); me.includeStandardAdditions = true

ObjC.import('Cocoa')

ObjC.registerSubclass({
  name: 'MainController',
  methods: {
    'appDidLaunch:': {
      types: ['void', ['id']],
      implementation: function(notification) {
        var appName = notification.userInfo.objectForKey('NSApplicationName').js
        me.activate()
        me.displayAlert(`Hello, ${appName}!`, {message: 'Nice to meet you.'})
        Application(appName).activate()
      }
    },
    'screensaverDidStop:': {
      types: ['void', ['id']],
      implementation: function(notification) {
        me.activate()
        me.displayAlert('Goodbye, screensaver!', {message: 'It was nice knowing you.'})
      }
    }
  }
})

var controller = $.MainController.new

$.NSWorkspace.sharedWorkspace.notificationCenter.addObserverSelectorNameObject(controller, 'appDidLaunch:', $.NSWorkspaceDidLaunchApplicationNotification, undefined)

$.NSDistributedNotificationCenter.defaultCenter.addObserverSelectorNameObject(controller, 'screensaverDidStop:', 'com.apple.screensaver.didstop', undefined)

Discussion

First, the applet code creates a new class named 'MainController', which implements two methods, 'appDidLaunch:' and 'screensaverDidStop:'. These methods are implemented to use the 'display alert' functionality from Standard Additions.

Next, the applet code instantiates an object of this class, and registers that instance as on observer of the notifications that are posted when apps are launched, and when the screensaver stops.

The applet continues to run after the JXA code executes, and when the events occur, the JXA functions are invoked.

Next Steps

If you want to run an AppleScript script from JXA, you can refer to the answer to this question.

If you want to make it harder to quit the applet accidentally, you can make the applet a 'UI Element' by setting the LSUIElement key to 'true' in the applet's Info.plist.

Finally, you might want to add the applet to the user's Login Items so that it starts automatically after a reboot.

bacongravy
  • 893
  • 8
  • 13
  • This is GREAT! It works perfectly, and has very low resource consumption, it seems like I could easily make the appDidLaunch function run through an if loop and ONLY pop the message if 'PRINTER.app' launches? So it wouldn't be everytime any application was opened, but only if someone hits the printer? – themightyrumpus Mar 29 '18 at 18:41
  • Yes, exactly! `if (appName == ‘PRINTER’) { ... }` – bacongravy Mar 29 '18 at 19:51
  • THANK YOU!! That works great, and the way you have the app name grabbed, all I need to do is `if (appName == 'PrinterProxy') ` and any printer I send to triggers the alert. Great work, thank you for taking the time @bacongravy – themightyrumpus Mar 29 '18 at 21:10