Run a folder action script when a file is copied into the folder

Hi

Moving on from a previous post, I have thought more about what I need in order to make may task easier.

Here is the folder setup

User

  • Documents
    • Clients
      • Client (Client-A)
        • Projects
          • Project (Project-B)
            • Design
            • Approvals
            • Portfolio
            • Production

I have named the client and project folders (Client-A & Project-B for test purposes)

You’ll notice that within the client’s project folder, there are 4 folders, Design, Approvals, Portfolio and Production.

The Script
Any files that are dragged into those 4 folders(Design, Approvals, Portfolio and Production) would automatically run a folder action script.

The script must rename the file with it’s parent (Project-B) but keep the extension. I also need to manually add a version number to the end of the name i.e. V1.

So

File-A.pdf would become Client-A V1.pdf

I have never used folder actions so I thought I would have a quick play. I have tried right clicking on the folder and clicking Services > Folder Action Setup > Run Service but it doesn’t give you the option to add your own script. How do I do this?

I’ve been accused of drip feeding my requirements rather than posting everything required. I understand why this is frustrating as an added requirement could mean a different approach to writing the script so I will try this approach instead.

I’m fairy new to Applescript and although I am starting to understand it, I don’t know where to start with this one.

Hello

before starting to code something I wish to point some details.
If I understand well you may have numerous folders named Project (akey).
As each folder contain 4 subfolders you will have to use 4 * (count projects) folder actions scripts because, if I remember well, such script may be attached to no more than a single folder.
I’m not sure that the folder actions scheme is able to apply to so many couples script/folder.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 30 juin 2020 11:53:00

Hi Yvan

That is correct, Each client’s project folder can have multiple projects i.e.

Client-A

  • Projects
    • Project-A
      • Design
      • Portfolio
      • Production
      • Approvals
    • Project-B
      • Design
      • Portfolio
      • Production
      • Approvals
    • Project-C
      • Design
      • Portfolio
      • Production
      • Approvals

As you will know, all new projects are created with a script (thanks to your help) which copies all those folders from a template folder so I guess the folder actions would need to be added to the original folders.

I’m not sure how folder actions work so, if a folder is copied does the folder action get copied with it?

Thank you.
It’s really what I guessed.
The folder actions script attached to a folder is not copied when we copy the folder.
But I was wrong on a point: a folder action may be attached to several folders.
What can’t be done is to work with several scripts attached to a folder.
In old days, attaching a script to a folder may be done with a script but I’m not sure that we may to that with not too old systems.
I will search in my archives.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 30 juin 2020 12:30:02

Appreciate your help

It seems that the content of your folder is the result of the instruction :

tell me to do shell script "cp -R " & ProjectTemplatePath & space & ProjectFolderPath

in your script New Project, VERSION 5.
Am I guessing correctly ?
If I am, it would be better to do the job directly in this script with no need for folder actions script.
Here I assume that the original items are available in the folder described by ProjectTemplatePath and that this one contain:
Design
— some files which need to be renamed in the duplicate process
Approvals
— some files which need to be renamed in the duplicate process
Portfolio
— some files which need to be renamed in the duplicate process
Production
— some files which need to be renamed in the duplicate process

I guess that because in the other thread we were asked to rename a file named blablah.md reached thru the hierarchy :
Documents
—Clients
——Amanda Rockley ESMT (AMAN01)
———Projects
————(AMAN01)-0001, Mitsubishi Outlander 2015
—————Design
——————blablah.md

The only difference is that you are now grabbing a part of the new Name from an other level in the hierarchy.
Always if I’m right, may we grab the complementary string from “(AMAN01)-0001, Mitsubishi Outlander 2015” and not from “Amanda Rockley ESMT (AMAN01)”.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 30 juin 2020 15:06:23

Apologies, I’ve not been at my computer.

You are right as in the folders are already in ‘ProjectTemplatePath’ but there will be no files apart from maybe one readme file.

All files within the folders design, approval, production and portfolio will be created once the client folder and template folders have been created/copied over from the template folders.

My process is as follows…

New client
• Register Client and Create client folder
• • Create a project ready
• • • Design Project
• • • • Save design file to folder (Any name I can think of)
• • • • • Rename design file based on project name

I hope that makes sense?

OK but I’m always skeptical.
When the design files are ready to save,
You must know the location where you want to save them.
What need to save them with ‘old names’, attach 4 folder action scripts designed to rename them according to a substring available in the path pointing to the final location?
You have a destination whose path is something like “Maciintosh HD:Users::Documents:Clients:Amanda Rockley ESMT (AMAN01):Projects:(AMAN01)-0006, Mitsubishi Outlander 2015:"
In this folder you have already created 4 subfolders.
You have a design file whose path is something like “blablabla:fileA.ext”
You want to duplicate it in HD:Users:
:Documents:Clients:Amanda Rockley ESMT (AMAN01):Projects:(AMAN01)-0006, Mitsubishi Outlander 2015:Production:” with the new name
“AMAN01 V1.ext”.
This may be done easily without your convoluted scheme.

If you post a precise list of these design files giving the name of the destination subfolder I will give you the wanted script in a snap.

In fact, such script is quite ready because I already wrote a script designed to grab files from a folder whose hierarchy was:
OS Folders
—Project Template
——Approvals
———file-A.jpg
———file-A.pdf
———file-A.txt
——Design
———file-A.jpg
———file-A.pdf
———file-A.txt
——Portfolio
———file-A.jpg
———file-A.pdf
———file-A.txt
——Production
———file-A.jpg
———file-A.pdf
———file-A.txt

which are replicated in the hierarchy:

Clients
—Amanda Rockley ESMT (AMAN01)
——Projects
———(AMAN01)-0001, Mitsubishi Outlander 2015
————Approvals
—————AMAN01 V1.jpg
—————AMAN01 V1.pdf
—————AMAN01 V1.txt
————Design
—————AMAN01 V1.jpg
—————AMAN01 V1.pdf
—————AMAN01 V1.txt
————Portfolio
—————AMAN01 V1.jpg
—————AMAN01 V1.pdf
—————AMAN01 V1.txt
————Production
—————AMAN01 V1.jpg
—————AMAN01 V1.pdf
—————AMAN01 V1.txt

I guess that there are no serious changes required to match your needs.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 1 juillet 2020 21:09:43

Complementary question, what is the nature of de “design files”?
Are they flat files or packages like some items generated by iWork ?
Packages aren’t treated exactly like flat files so, if you may have some of them additional code is required.

Back to folder actions script.
Your proposal is the worst kind of task asked to such tool.
When an attached folder actions script is asked to rename an item, the renamed object is analyzed as a newly dropped item and the script is triggered one more time.
Most of the time we may use a workaround : move the item in a subfolder before renaming it but this scheme doesn’t fit your needs.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 juillet 2020 10:02:01

Here is my proposal.
I commented quite each instruction so that you may easily understand what is done.
ASObjC is verbose but it’s really efficient.

-- Danjuan & YK
--> New Design
--> VERSION 8

(*
Below is an example of the hierarchy in which were previously created the design items.
The scrip is able to treat numerous design items which may be falt files or packages
OS Folders
—Project Template
——Approvals
———file-A.jpg
———file-A.pdf
———file-B.pdf -- would be a problem if changeAllName is true
——Design
———file-A.jpg
———file-A.pdf
———file-A.txt
——Portfolio
———file-A.jpg
———file-A.pdf
———file-A.txt
——Production
———file-A.jpg
———file-A.pdf - a flat file
———file-A.rtfd - a package

Select a project which need to be updated, for instance :

Clients
—Amanda Rockley ESMT (AMAN01)
——Projects
———(AMAN01)-0001, Mitsubishi Outlander 2015 <-- the selected project
————Approvals - not required
————Design - required only if it must host a ReadMe file
—————ReadMe.rtf

Execute the script.
That's all folks.

Yvan KOENIG (VALLAURIS, France) jeudi 2 juillet 2020 16:49:02
*)
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

property forYK : true
-- true -- version used by YK for tests
-- false -- more or less your code without the git part

property changeAllName : false
-- true -- change the entire name of files minus the extension
-- false -- Append the baseName at the beginning of the file names

property dropParens : true
-- true --> drop the parens surrounding baseName --> "AMAN01"
-- false --> keep the parens surrounding baseName --> "(AMAN01)"

if forYK then
	set PDesignsPath to (path to desktop as string) & "Designs:OS Folders:Project Template:"
else
	set PDesignsPath to (path to desktop as string) & "Designs:OS Folders:Project Template:" -- Edit to fit your needs
end if

tell application "Finder"
	set ProjectFolderPath to (item 1 of (get the selection)) as string --> "SSD 1000:Users:**********:Documents:Clients:Amanda Rockley ESMT (AMAN01):Projects:(AMAN01)-0001, Mitsubishi Outlander 2015:"
end tell -- "Finder"

set baseName to my getbaseNameFrom:ProjectFolderPath

if baseName = "" then error " “" & someString & "” isn’t a project folder."

-- Define several constants
set fileManager to current application's NSFileManager's defaultManager()
set skipsPackageDescendants to current application's NSDirectoryEnumerationSkipsPackageDescendants as integer --> 2
set skipsHiddenFiles to current application's NSDirectoryEnumerationSkipsHiddenFiles as integer --> 4
set theOptions to skipsPackageDescendants + skipsHiddenFiles
set NSURLIsDirectoryKey to current application's NSURLIsDirectoryKey
set NSURLIsPackageKey to current application's NSURLIsPackageKey
set keysToRequest to {NSURLIsDirectoryKey, NSURLIsPackageKey}
-- Grab the content of the folder containing the design files
set theURLs to (fileManager's enumeratorAtURL:(PDesignsPath as «class furl») includingPropertiesForKeys:keysToRequest options:theOptions errorHandler:(missing value))'s allObjects()
if (count theURLs) = 0 then error "Le dossier “" & hfsPath & "” est vide."
-- build URL of the project to update
set projectURL to current application's NSURL's fileURLWithPath:(POSIX path of ProjectFolderPath)
-- grab the name of the project
set projectName to projectURL's lastPathComponent() as string
-- Loop duplicating the design files (or packages)
repeat with aURL in theURLs
	set {theResult, isDirectory} to (aURL's getResourceValue:(reference) forKey:NSURLIsDirectoryKey |error|:(missing value))
	if isDirectory as boolean then -- it's a folder or a package.
		-- is this directory a package ?
		set {theResult, isPackage} to (aURL's getResourceValue:(reference) forKey:NSURLIsPackageKey |error|:(missing value))
		if isPackage as boolean then
			set designURL to aURL -- we will duplicate it with a new name
		else -- It's a folder
			set designURL to missing value
		end if
	else -- its a flat file
		set designURL to aURL -- we will duplicate it with a new name
	end if
	if designURL is not missing value then
		-- designURL is an URL pointing upon a design item to duplicate with a new name
		-- grab the URL of the folder containing the design item
		set sourceFolderURL to designURL's URLByDeletingLastPathComponent()
		-- grab the name of the folder containing the design item
		set sourceFolderName to sourceFolderURL's lastPathComponent() as string
		-- build the URL of the folder where the new design item will be stored
		set destFolderURL to (projectURL's URLByAppendingPathComponent:sourceFolderName)
		-- if necessary, create the destination folder
		set {theResult, theError} to (fileManager's createDirectoryAtURL:destFolderURL withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
		-- build the new name according to the property changeAllName
		if changeAllName then
			-- grab the name extension of the design item
			set theExt to designURL's pathExtension() as string
			set newName to baseName & " V1." & theExt
		else
			-- grab the name of the design item
			set designName to designURL's lastPathComponent() as string
			set newName to baseName & " V1 " & designName
		end if
		-- build the URL of the newly named design item
		set theNewURL to (destFolderURL's URLByAppendingPathComponent:newName)
		-- try to copy the newly renamed design item
		set {theResult, theError} to (fileManager's copyItemAtURL:designURL toURL:theNewURL |error|:(reference))
		if (theResult as boolean) is false then
			if theError's code() as integer = 516 then -- the item already exists
				(fileManager's removeItemAtURL:theNewURL |error|:(reference)) -- removes the old version
				set {theResult, theError} to (fileManager's copyItemAtURL:designURL toURL:theNewURL |error|:(reference))
			else
				error (theError's localizedDescription() as string)
			end if
		end if
	end if -- designURL …
end repeat

display notification "The project “" & projectName & "” is updated."

#===== Now we aren't in the main code, we may put the handlers

on getbaseNameFrom:someString
	-- define the pattern used by the regular expression
	set pattern to "\\(([A-Z]{4}[0-9]{2})\\)"
	-- build the regular expression
	set regex to (current application's NSRegularExpression's regularExpressionWithPattern:pattern options:0 |error|:(missing value))
	-- call the regex to search the substring mathching the given pattern
	set matches to (regex's matchesInString:someString options:0 range:{location:0, |length|:(count someString)})
	-- matches is an object which can't be used directly by AppleScript
	-- convert the AppleScript string object into an NSString which is the official class used by ASObjC
	set cocoaString to (current application's NSString's stringWithString:someString)
	set baseName to "" -- in case we selected a wrong folder
	repeat with aMatch in matches
		-- extract the Applescript's substrings
		if dropParens then
			set baseName to (cocoaString's substringWithRange:(aMatch's rangeAtIndex:1)) as string --> "AMAN01"
		else
			set baseName to (cocoaString's substringWithRange:(aMatch's range())) as string --> "(AMAN01)"
		end if
	end repeat
	return baseName
end getbaseNameFrom:

#=====

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 2 juillet 2020 17:08:34

Thank you

Sorry I’ve been working.

I’ve tried to test it quickly but I can’t get it to work. It throws up the following error.


error "Can’t make \"Volumes:GoogleDrive:My Drive:Work:Resources:Templates:OS Folders:Project Template:\" into type constant." number -1700 from "Volumes:GoogleDrive:My Drive:Work:Resources:Templates:OS Folders:Project Template:" to constant

I tried adding my own path:


property forYK : false

if forYK then
	set PDesignsPath to (path to desktop as string) & "Designs:OS Folders:Project Template:"
else
	set PDesignsPath to (path to "Volumes:GoogleDrive:My Drive:Work:Resources:Templates:OS Folders:Project Template:" as string)
end if

First point, this syntax which I saw in several of your scripts is wrong.
The word “Volumes” at the very beginning of a pathname makes sense in a POSIX Path, not in an Hfs one.
Here a correct syntax would be :

set PDesignsPath to ("GoogleDrive:My Drive:Work:Resources:Templates:OS Folders:Project Template:" as string)

Point 2. This correct syntax wouldn’t be the correct needed instruction.
If I remember well, the described folder is the one where you were supposed to store the templates which you wrote that they aren’t the items which must be used here.
The needed path is the one of the folder in which, if I understood the description of your process, you built your design files.
You wrote:
My process is as follows…

New client
• Register Client and Create client folder
• • Create a project ready
• • • Design Project
• • • • Save design file to folder (Any name I can think of)
• • • • • Rename design file based on project name

I understood that you create your design files in a dedicated folder from which you duplicated them to the final project folder.
It’s the only scheme which made sense because if you create these items in the “Templates:OS Folders:Project Template:” folder these design items would be annoying the next time you would use this folder.
You didn’t defined the location of this folder so I created one of my own : (path to desktop as string) & “Designs:OS Folders:Project Template:”.
In fact it’s a too much complicated path, (path to desktop as string) & “Designs:” would have been better but I already had my own “Designs” folder on my Desktop and I didn’t wished to clobber it.
I may understand that you are busy but this doesn’t prevent you to take care of what you do or write.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) vendredi 3 juillet 2020 15:57:30