Folder Actions: processing only user-added items

Folder Actions are potentially very useful, although their current implementation leaves a little to be desired.

For example, if the target folder of a folder action is moved, it is assumed that the folder’s entire contents have again been added to it - so they are all processed once more. Also, if an invisible file is automatically added (such as when Finder decides to add an invisible “.DS_Store” file to the folder), the addition is processed as if added by the user - although he/she has no direct control over the event or its timing. (Even a renamed item is regarded as having been newly added.)

This behaviour seems somewhat buggy, and hopefully a fix will be forthcoming in due course. But in the meantime, what can we do about it?

The workarounds I’ve considered have included storing (in a property) a folder’s path or its current contents - and then comparing the values to those in force at the time the folder action is triggered. However, while such techniques might work tolerably well, they have a couple of significant drawbacks - namely:

An alternative method might be to check for invisible items in the added list. Since these can’t be added by the user manually, their presence indicates that the current list of additions is the result of some other action. (While this technique doesn’t address the file renaming issue, it should fix the other anomalies mentioned.)

There are a couple of points about the following workaround worth mentioning. If the target folder initially contains no invisible files, then the script can’t really distinguish between items being added and the folder itself being moved. In this event, the script resorts to asking the user to confirm the action taken. However, this should occur only once, since the folder is simultaneously ‘marked’ with an invisible file (“.FA_Marker”) - which should then aid the accurate identification of subsequent actions.

To incorporate the suggestion in your script, simply insert the ‘itemsAdded’ script object at the beginning of your script. Then wrap the contents of your ‘adding folder items to’ subroutine in a ‘validList of itemsAdded’ conditional block - something like this:

script itemsAdded
	property utxt_return : return as Unicode text
	property an_invisible : utxt_return & "."
	
	on firstAction for this_folder
		tell application "Finder"
			make new file at folder this_folder with properties {name:".FA_Marker"}
			with timeout of weeks seconds
				button returned of (display alert "Please confirm the action you are perfoming:" buttons ¬
					{"Adding Items", "Moving Folder"} message "Since this is the first folder action for \"" & this_folder's name & ¬
					"\", please clarify which action you are currently taking. (This message should not appear again.)")
			end timeout
		end tell
	end firstAction
	
	on validList from items_added to this_folder
		set tid to text item delimiters
		set text item delimiters to utxt_return
		set item_list to utxt_return & items_added
		set text item delimiters to utxt_return & this_folder
		set item_list to item_list's text items
		set text item delimiters to utxt_return
		set item_list to item_list as Unicode text
		set folder_list to utxt_return & (list folder this_folder)
		set text item delimiters to tid
		considering case
			an_invisible is not in item_list and (an_invisible is in folder_list or (firstAction for this_folder) is "Adding Items")
		end considering
	end validList
end script

on adding folder items to this_folder after receiving this_list
	if validList of itemsAdded to this_folder from this_list then
		
		(* add your required processing here - this example for demonstration purposes only *)
		tell application "Finder" to repeat with this_item in this_list
			display dialog ((this_item's kind as Unicode text) & " \"" & this_item's name & "\"") giving up after 2
		end repeat (* end of example *)
		
	end if
end adding folder items to

I’ve just rediscovered this, having downloaded it to look at earlier in the month. What a brilliant idea! The English-likeness is very clever (if a little misleading at one point!) and it’s a nice touch giving users a week to decide what to do about the “first action” dialog. :wink:

I think it’s possible to simplify the ‘validList’ handler as follows.

1.) When the ‘items_added’ list is coerced to Unicode, any added invisibles can be discovered by means of the sequence “:.” in the result.

2.) The secondary test can compare the number of items in the folder with the number that have been added, rather than specifically checking for non-added invisibles.

script itemsAdded
	on firstAction for this_folder
		tell application "Finder"
			make new file at folder this_folder with properties {name:".FA_Marker"}
			with timeout of weeks seconds
				button returned of (display alert "Please confirm the action you are perfoming:" buttons ¬
					{"Adding Items", "Moving Folder"} message "Since this is the first folder action for \"" & this_folder's name & ¬
					"\", please clarify which action you are currently taking. (This message should not appear again.)")
			end timeout
		end tell
	end firstAction
	
	on validList from items_added to this_folder
		considering case
			set tid to text item delimiters
			set text item delimiters to ""
			set no_invisibles_added to (items_added as Unicode text does not contain ":.")
			set text item delimiters to tid
			(no_invisibles_added) and ¬
				(((count (list folder this_folder)) > (count items_added)) or ((firstAction for this_folder) is "Adding Items"))
		end considering
	end validList
end script

on adding folder items to this_folder after receiving this_list
	if validList of itemsAdded to this_folder from this_list then
		
		(* add your required processing here - this example for demonstration purposes only *)
		tell application "Finder" to repeat with this_item in this_list
			display dialog ((this_item's kind as Unicode text) & " \"" & this_item's name & "\"") giving up after 2
		end repeat (* end of example *)
		
	end if
end adding folder items to

‘removing folder items from’ actions can be triggered too if their watched folders are moved while they contain anything.

Kai’s original validation code works for both added and removed items. My comparison of the number of items in the folder with the number of items ‘added’ doesn’t, but checking for invisibles is also possible by comparing the returns from ‘list folder’ and ‘list folder … without invisibles’. So with a little rephrasing, a dual-purpose checker is possible.

But if you don’t particularly need a dual-use method, ‘on removing’ actions can be validated far more quickly and easily by seeing if the ‘removed items’ are still in the folder:

on validChanges to this_folder from this_list
	considering case
		set item_path to (beginning of this_list) as Unicode text
		if (item_path ends with ":") then set item_path to text 1 thru -1 of item_path
		set folder_path to this_folder as Unicode text
		
		(item_path does not start with folder_path) or (text ((count folder_path) + 1) thru -1 of item_path contains ":")
	end considering
end validChanges

on removing folder items from this_folder after losing this_list
	if (validChanges to this_folder from this_list) then
		
		(* add your required processing here *)
		
	end if
end removing folder items from

Since I haven’t revisited this subject for a while, I wasn’t aware of your suggested refinements, Nigel. However, I recently had cause to remind myself about the issues involved - and, as ever, your observations are bang on the button. Thanks for the improvements! :smiley:

As for the time allowed to choose a response to the “first action” dialog, well… such weighty decisions can sometimes require very careful consideration… :stuck_out_tongue: