Commit 608e3bf8 authored by Joel Rennich's avatar Joel Rennich

Actions in working order

parent 9d72f98e
...@@ -113,15 +113,15 @@ ...@@ -113,15 +113,15 @@
<CommandLineArguments> <CommandLineArguments>
<CommandLineArgument <CommandLineArgument
argument = "-v" argument = "-v"
isEnabled = "YES"> isEnabled = "NO">
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "-shares" argument = "-shares"
isEnabled = "YES"> isEnabled = "NO">
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "-rawLDAP" argument = "-rawLDAP"
isEnabled = "YES"> isEnabled = "NO">
</CommandLineArgument> </CommandLineArgument>
</CommandLineArguments> </CommandLineArguments>
<AdditionalOptions> <AdditionalOptions>
......
...@@ -18,9 +18,13 @@ An action is comprised of some meta data and then four phases. Each phase has a ...@@ -18,9 +18,13 @@ An action is comprised of some meta data and then four phases. Each phase has a
| Action | Command Set that make up the actual Action itself | Array | no | Action | Command Set that make up the actual Action itself | Array | no
| Post | Command Set that will happen after the Action commands are run | Array | no | Post | Command Set that will happen after the Action commands are run | Array | no
| GUID | Unique ID for the Action | String | no | GUID | Unique ID for the Action | String | no
|Connected | If the action set should only be run when connected to the AD domain | Bool | true
|Timer| Length in minutes between firing the Action | Int | 15
|ToolTip| The text to be shown when hovering over the menu item | String | Click here for support
* Note that the Title command set can only have one command * Note that the Title command set can only have one command
* An Action with the Name of "Separtor" will become a separator bar in the menu. * If the Title command returns "false" or "true" the text of the title won't be updated. Instead a red, in the case of "false", or green dot will be next to the menu item and the title will be the Name of the action set.
* An Action with the Name of "Separator" will become a separator bar in the menu.
## Commands ## Commands
...@@ -34,11 +38,10 @@ Each command has a CommandOptions value that determines what the command does. A ...@@ -34,11 +38,10 @@ Each command has a CommandOptions value that determines what the command does. A
| app | Launch an app at a specific file path | The path to the application | app | Launch an app at a specific file path | The path to the application
| url | Launch a URL in the user's default browser | The URL to launch | url | Launch a URL in the user's default browser | The URL to launch
| ping | Ping a host, will return false if the host is unpingable | The host to ping | ping | Ping a host, will return false if the host is unpingable | The host to ping
| srv | Lookup up SRV records, returning false if they can't be found | The SRV records to lookup
| adgroup | Determine if the current user is a member of an AD group | The group to test with | adgroup | Determine if the current user is a member of an AD group | The group to test with
| alert | Display a modal dialog to the user | Text of the dialog | alert | Display a modal dialog to the user | Text of the dialog
|notify| Display a notification in the notification center | Text of the notification
* Note that Post action sets with an alert will automatically show the results of the Action set. |false| A command that always returns false | Anything
## Workflow ## Workflow
...@@ -53,7 +56,6 @@ Each command has a CommandOptions value that determines what the command does. A ...@@ -53,7 +56,6 @@ Each command has a CommandOptions value that determines what the command does. A
There's a few more features that we'd like to get down before release. Currently all of these are achievable. There's a few more features that we'd like to get down before release. Currently all of these are achievable.
* Timers - Schedule the execution of an Action based upon a repeating time. Note that you can have "silent" Actions that do not show up in the Actions Menu, but do execute on a repeated schedule.
* Triggers - Trigger actions based upon system events such as: * Triggers - Trigger actions based upon system events such as:
* Network change * Network change
* Status images - Red/yellow/green dots next to menu items based upon the Show command set of the item. Currently you can "cheat" and use emoji in your menu titles. * Action -> Post - Allow actions to send messages/status to the Post command set
\ No newline at end of file
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.1.3.865.865.865.867.867.869.869.869</string> <string>1.1.3</string>
<key>CFBundleShortVersionString-orginal</key> <key>CFBundleShortVersionString-orginal</key>
<string>1.1.3.865.865.865.867.867.869.869.869</string> <string>1.1.3.871.871.871.871.871.871</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
......
...@@ -21,21 +21,28 @@ class NoMADAction : NSObject { ...@@ -21,21 +21,28 @@ class NoMADAction : NSObject {
// actions // actions
var showTest: [Dictionary<String, String?>]? = nil var show: [Dictionary<String, String?>]? = nil
var title: Dictionary<String, String?>? = nil var title: Dictionary<String, String?>? = nil
var action : [Dictionary<String, String?>]? = nil var action : [Dictionary<String, String?>]? = nil
var post : [Dictionary<String, String?>]? = nil var post : [Dictionary<String, String?>]? = nil
var preType: String? = nil // timers and triggers
var preTypeOptions: [String]? = nil
var timer : Int? = nil
var timerObject : Timer? = nil
var trigger : [String]? = nil
// status
var actionType: String? = nil var status: String? = nil
var actionTypeOptions: [String]? = nil var visible: Bool = true
var connected: Bool = false
// globals // globals
var display : Bool = false var display : Bool = false
var text : String = "action item" var text : String = "action item"
var tip : String = ""
// init // init
...@@ -73,19 +80,16 @@ class NoMADAction : NSObject { ...@@ -73,19 +80,16 @@ class NoMADAction : NSObject {
return actionName return actionName
} }
return runActionCommand(action: title!["Command"] as? String ?? "none", options: title!["CommandOptions"] as? String ?? "none") let result = runActionCommand(action: title!["Command"] as? String ?? "none", options: title!["CommandOptions"] as? String ?? "none")
}
if result == "true" {
func preTest() { status = "green"
if preType != nil { return actionName
switch preType { } else if result == "false" {
case "group"? : status = "red"
if defaults.array(forKey: Preferences.groups)?.contains(where: { $0 as! String == "admin" }) ?? false { return actionName
print("group passed") } else {
} return result
default :
break
}
} }
} }
...@@ -104,8 +108,9 @@ class NoMADAction : NSObject { ...@@ -104,8 +108,9 @@ class NoMADAction : NSObject {
if post != nil { if post != nil {
// run any post commands // run any post commands
// TODO: add in a way to report on result of Action
_ = runCommand(commands: post)
} }
} }
......
...@@ -58,6 +58,28 @@ public func runActionCommand( action: String, options: String) -> String { ...@@ -58,6 +58,28 @@ public func runActionCommand( action: String, options: String) -> String {
} else { } else {
return "false" return "false"
} }
case "alert" :
// show an alert
let myAlert = NSAlert()
myAlert.messageText = options
// move to the foreground since we're displaying UI
DispatchQueue.main.async {
myAlert.runModal()
}
case "notify" :
let notification = NSUserNotification()
notification.informativeText = options
notification.hasReplyButton = false
notification.hasActionButton = false
notification.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default.deliver(notification)
case "false" :
return "false"
default : default :
break break
} }
......
...@@ -10,6 +10,8 @@ import Cocoa ...@@ -10,6 +10,8 @@ import Cocoa
let nActionMenu = NoMADActionMenu() let nActionMenu = NoMADActionMenu()
let actionMenuQueue = DispatchQueue(label: "menu.nomad.NoMAD.actions", attributes: [])
// class to create a menu of all the actions // class to create a menu of all the actions
...@@ -17,7 +19,7 @@ let nActionMenu = NoMADActionMenu() ...@@ -17,7 +19,7 @@ let nActionMenu = NoMADActionMenu()
// globals // globals
@objc public let actionMenu = NSMenu() @objc public var actionMenu = NSMenu()
var actions = [NoMADAction]() var actions = [NoMADAction]()
let sharePrefs: UserDefaults? = UserDefaults.init(suiteName: "menu.nomad.actions") let sharePrefs: UserDefaults? = UserDefaults.init(suiteName: "menu.nomad.actions")
...@@ -27,9 +29,9 @@ let nActionMenu = NoMADActionMenu() ...@@ -27,9 +29,9 @@ let nActionMenu = NoMADActionMenu()
static let kPrefVersion = "Version" static let kPrefVersion = "Version"
static let kPrefActions = "Actions" static let kPrefActions = "Actions"
// update actions // load the actions
func update() { func load() {
// read in the preferences // read in the preferences
...@@ -50,13 +52,20 @@ let nActionMenu = NoMADActionMenu() ...@@ -50,13 +52,20 @@ let nActionMenu = NoMADActionMenu()
for action in rawPrefs { for action in rawPrefs {
// if we already know about it bail
guard let actionName = action["Name"] as? String else { continue } guard let actionName = action["Name"] as? String else { continue }
let newAction = NoMADAction.init(actionName, guid: action["GUID"] as? String ?? nil) let newAction = NoMADAction.init(actionName, guid: action["GUID"] as? String ?? nil)
newAction.showTest = action["ShowTest"] as? [Dictionary<String,String?>] ?? nil newAction.show = action["Show"] as? [Dictionary<String,String?>] ?? nil
newAction.action = action["Action"] as? [Dictionary<String,String?>] ?? nil newAction.action = action["Action"] as? [Dictionary<String,String?>] ?? nil
newAction.title = action["Title"] as? Dictionary<String,String?> ?? nil newAction.title = action["Title"] as? Dictionary<String,String?> ?? nil
newAction.post = action["Post"] as? [Dictionary<String,String?>] ?? nil
newAction.timer = action["Timer"] as? Int ?? nil
newAction.tip = action["ToolTip"] as? String ?? ""
newAction.connected = action["Connected"] as? Bool ?? false
// add in all options // add in all options
...@@ -64,38 +73,107 @@ let nActionMenu = NoMADActionMenu() ...@@ -64,38 +73,107 @@ let nActionMenu = NoMADActionMenu()
} }
} }
@objc func updateActions(_ connected: Bool=false) {
if actions.count < 1 {
// nothing to update
return
}
actionMenuQueue.async(execute: {
for action in self.actions {
if action.connected && !connected {
action.display = false
continue
}
if action.timerObject == nil && action.timer != nil {
// set up the timer
action.timerObject = Timer.init(timeInterval: TimeInterval.init(action.timer! * 60), target: action, selector: #selector(action.runAction), userInfo: nil, repeats: true)
RunLoop.main.add(action.timerObject!, forMode: .commonModes)
}
action.display = action.runCommand(commands: action.show)
action.text = action.getTitle()
}
})
}
// create menu // create menu
@objc func createMenu() { @objc func createMenu() {
for action in self.actions {
//let itemAction = #selector(action.action)
if action.actionName.lowercased() == "separator" {
let separator = NSMenuItem.separator()
self.actionMenu.addItem(separator)
} else {
let menuItem = NSMenuItem.init()
menuItem.title = action.text
if action.status != nil {
switch action.status {
case "red"? :
menuItem.image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusUnavailable.rawValue)
case "green"? :
menuItem.image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusAvailable.rawValue)
case "yellow"? :
menuItem.image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusPartiallyAvailable.rawValue)
default:
break
}
}
if !action.display {
menuItem.isHidden = true
}
menuItem.target = action
menuItem.action = #selector(action.runAction)
menuItem.isEnabled = true
menuItem.toolTip = action.tip
menuItem.state = NSControl.StateValue(rawValue: 0)
self.actionMenu.addItem(menuItem)
}
}
}
func updateMenu() {
actionMenu.removeAllItems() if actionMenu.items.count == 0 {
return
}
for action in actions { for i in 0...(actionMenu.items.count - 1 ) {
actionMenu.items[i].title = actions[i].text
//let itemAction = #selector(action.action) if actions[i].status != nil {
switch actions[i].status {
if !action.runCommand(commands: action.showTest) { case "red"? :
continue actionMenu.items[i].image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusUnavailable.rawValue)
case "green"? :
actionMenu.items[i].image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusAvailable.rawValue)
case "yellow"? :
actionMenu.items[i].image = NSImage.init(imageLiteralResourceName: NSImage.Name.statusPartiallyAvailable.rawValue)
default:
break
}
} }
if action.actionName.lowercased() == "separator" { if !actions[i].display {
let separator = NSMenuItem.separator() actionMenu.items[i].isHidden = true
actionMenu.addItem(separator)
} else { } else {
let menuItem = NSMenuItem.init() actionMenu.items[i].isHidden = false
menuItem.title = action.getTitle()
menuItem.target = action
menuItem.action = #selector(action.runAction)
menuItem.isEnabled = true
menuItem.toolTip = "A NoMAD custom action"
menuItem.state = NSControl.StateValue(rawValue: 0)
actionMenu.addItem(menuItem)
} }
} }
//print(actionMenu)
//actionMenu.autoenablesItems = false
// return actionMenu
} }
@IBAction func actionClick(_ sender: AnyObject) { @IBAction func actionClick(_ sender: AnyObject) {
......
...@@ -151,6 +151,10 @@ class NoMADMenuController: NSObject, LoginWindowDelegate, PasswordChangeDelegate ...@@ -151,6 +151,10 @@ class NoMADMenuController: NSObject, LoginWindowDelegate, PasswordChangeDelegate
shareMounterMenu.updateShares(connected: self.userInformation.connected) shareMounterMenu.updateShares(connected: self.userInformation.connected)
// load up the actions
nActionMenu.load()
print(defaults.integer(forKey: Preferences.autoRenewCert)) print(defaults.integer(forKey: Preferences.autoRenewCert))
// set up Icons - we need 2 sets of 2 for light and dark modes // set up Icons - we need 2 sets of 2 for light and dark modes
...@@ -1408,19 +1412,33 @@ class NoMADMenuController: NSObject, LoginWindowDelegate, PasswordChangeDelegate ...@@ -1408,19 +1412,33 @@ class NoMADMenuController: NSObject, LoginWindowDelegate, PasswordChangeDelegate
// ACTIONS // ACTIONS
nActionMenu.update() // update the actions first
if !self.NoMADMenu.items.contains(self.myActionsMenu) { nActionMenu.updateActions(self.userInformation.connected)
self.myActionsMenu.title = defaults.string(forKey: Preferences.menuActions) // pivot on if the menu exists or not
nActionMenu.createMenu()
self.myActionsMenu.submenu = nActionMenu.actionMenu if !self.NoMADMenu.items.contains(self.myActionsMenu) {
self.NoMADMenu.addItem(self.myActionsMenu)
self.myActionsMenu.isEnabled = true
} else {
nActionMenu.createMenu() nActionMenu.createMenu()
if nActionMenu.actionMenu.items.count > 0 {
// we have a menu add it to the main menu
self.myActionsMenu.title = defaults.string(forKey: Preferences.menuActions) ?? "Actions"
self.myActionsMenu.submenu = nActionMenu.actionMenu
let lockIndex = self.NoMADMenu.index(of: self.NoMADMenuLockScreen)
self.NoMADMenu.insertItem(self.myActionsMenu, at: (lockIndex + 1 ))
}
} else {
nActionMenu.updateMenu()
} }
if self.userInformation.status == "Logged In" { if self.userInformation.status == "Logged In" {
self.myShareMenuItem.title = defaults.string(forKey: Preferences.menuFileServers) ?? "FileServers".translate self.myShareMenuItem.title = defaults.string(forKey: Preferences.menuFileServers) ?? "FileServers".translate
......
...@@ -87,7 +87,7 @@ class ShareMounterMenu: NSObject { ...@@ -87,7 +87,7 @@ class ShareMounterMenu: NSObject {
} }
if CommandLine.arguments.contains("-shares") { if CommandLine.arguments.contains("-shares") {
print("***Share Menuu***") print("***Share Menu***")
print(myShareMenu) print(myShareMenu)
} }
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
<dict> <dict>
<key>Name</key> <key>Name</key>
<string>update test</string> <string>update test</string>
<key>Connected</key>
<true/>
<key>Title</key> <key>Title</key>
<dict> <dict>
<key>Command</key> <key>Command</key>
...@@ -16,7 +18,7 @@ ...@@ -16,7 +18,7 @@
<key>CommandOptions</key> <key>CommandOptions</key>
<string>/bin/cat /tmp/titleText</string> <string>/bin/cat /tmp/titleText</string>
</dict> </dict>
<key>ShowTest</key> <key>Show</key>
<array> <array>
<dict> <dict>
<key>Command</key> <key>Command</key>
...@@ -38,29 +40,40 @@ ...@@ -38,29 +40,40 @@
<dict> <dict>
<key>Name</key> <key>Name</key>
<string>non-working ping test</string> <string>non-working ping test</string>
<key>ShowTest</key> <key>ToolTip</key>
<string>This is a ping test</string>
<key>Title</key>
<dict>
<key>Command</key>
<string>ping</string>
<key>CommandOptions</key>
<string>dc1.nomad.test</string>
</dict>
<key>Action</key>
<array> <array>
<dict> <dict>
<key>Command</key> <key>Command</key>
<string>ping</string> <string>path</string>
<key>CommandOptions</key> <key>CommandOptions</key>
<string>dc8.nomad.test</string> <string>/usr/bin/touch /tmp/action1</string>
</dict> </dict>
</array> </array>
<key>Action</key> <key>Post</key>
<array> <array>
<dict> <dict>
<key>Command</key> <key>Command</key>
<string>path</string> <string>alert</string>
<key>CommandOptions</key> <key>CommandOptions</key>
<string>/usr/bin/touch /tmp/action1</string> <string>This is just a test</string>
</dict> </dict>
</array> </array>
</dict> </dict>
<dict> <dict>
<key>Name</key> <key>Name</key>
<string>Working ping test</string> <string>Working ping test</string>
<key>ShowTest</key> <key>Timer</key>
<integer>5</integer>
<key>Show</key>
<array> <array>
<dict> <dict>
<key>Command</key> <key>Command</key>
...@@ -78,10 +91,29 @@ ...@@ -78,10 +91,29 @@
<string>/usr/bin/touch /tmp/action2</string> <string>/usr/bin/touch /tmp/action2</string>
</dict> </dict>
</array> </array>
<key>Post</key>
<array>
<dict>
<key>Command</key>
<string>alert</string>
<key>CommandOptions</key>
<string>This is just a test!</string>
</dict>
<dict>
<key>Command</key>
<string>notify</string>
<key>CommandOptions</key>
<string>Notification test</string>
</dict>
</array>
</dict>
<dict>
<key>Name</key>
<string>separator</string>
</dict> </dict>
<dict> <dict>
<key>Name</key> <key>Name</key>
<string>Placeholder</string> <string>Just a message</string>
</dict> </dict>
</array> </array>
</dict> </dict>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment