Good day everyone!
Mark Webber is on pole position for the third time this season and I finished my FTP update script this morning, so today is definitely a wonderful day.
But of course this is not the forum to talk about formula 1.
Some time ago I started learning HTML, CSS, PHP and JavaScript to build my site with my own code and it’s going pretty well.
Before I coded the webpages myself I used RapidWeaver to build them. The feature I liked the most was the updating aspect of it. It only updated the pages I had edited and in that way reduced the time it took to upload all webpages. Now that I’m writing the webpages myself, I of course don’t use RapidWeaver anymore. Before this script I had to remember which pages I had edited and then I had to upload them one by one with Cyberduck. I got sick of it and since every Mac comes with AppleScript, I thought of making a script which only updates the files I edited.
The script only works with FTP servers and uses cURL to upload files and make directories. To use this script, you’ll also need the web functions I made and I script file which saves the modification date of the updated files.
An almost ready-to-use version is available at http://developerief2.site11.com/Public-Files/Site_Update.zip. The only thing you’ll have to do to make it work, is open it with ScriptEditor and change the properties in the beginning of the script.
Site Uploader.app/Contents/Resources/Scripts/main.scpt
-- ================================================
-- =================== DEFAULTS
-- ================================================
property SITE_FTP_IP_ADDRESS : "1.1.1.1"
property SITE_FTP_USERNAME : "username"
property SITE_FTP_ROOT_DIRECTORY : "1.1.1.1/public_html/"
property HTML_FOLDER : missing value
property FILE_INDEX_SCRIPT : ((path to me as text) & "Contents:Resources:file-index.scpt") as alias
property DEST_FOLDER : "/"
property LOG_FILE : ((path to me as text) & "Contents:Resources:log.txt") as string
-- ================================================
-- =================== GLOBALS
-- ================================================
global _loadedScript
global _sitePassword
global _errorCount
global _updatedCount
global _silenceMode
-- ================================================
-- =================== MAIN
-- ================================================
on run
set HTML_FOLDER to CheckValidFolder(HTML_FOLDER, "Choose the folder containing the HTML source files")
set _errorCount to 0
set _updatedCount to 0
set _sitePassword to AskForPassword("The password of the FTP server is needed")
set _silenceMode to (button returned of (display dialog "Would you like to run in silence mode?" buttons {"Yes", "No"} default button 2)) is "Yes"
set _loadedScript to LoadFileIndex()
Main(HTML_FOLDER, DEST_FOLDER)
SaveFileIndex(_loadedScript)
if _silenceMode is false then display alert ((_errorCount & " errors have occurred." & return & _updatedCount & " files have been updated.") as string)
end run
-- ================================================
-- =================== HANDLERS
-- ================================================
on Main(srcFolder)
set srcFolder to srcFolder as alias
set WebFunctions to LoadWebFunctions()
-- get entire contents
set entireFolderContents to list folder srcFolder
-- loop to folder contents
repeat with anItemName in entireFolderContents
-- construct full path
set anItem to ((srcFolder as text) & anItemName) as alias
-- get info
set itemInfo to (info for anItem)
-- construct destination folder path
set sourceFilePathComponents to PathComponents(POSIX path of anItem)
set sourceFolderPathComponents to PathComponents(POSIX path of HTML_FOLDER)
set pathComponentsBetween to ((count sourceFilePathComponents) - (count sourceFolderPathComponents) - 1)
set destFolder to DEST_FOLDER
set componentsToAdd to {}
if pathComponentsBetween is not 0 then
set componentsToAdd to items ((count sourceFolderPathComponents) + 1) thru (((count sourceFolderPathComponents) + 1) + (pathComponentsBetween - 1)) of sourceFilePathComponents
repeat with i in componentsToAdd
set destFolder to StringByAppendingPathComponent(POSIX path of destFolder, (i as string))
end repeat
end if
destFolder
if destFolder is not "/" then WebFunctions's MakeFolderOnFTPServer(SITE_FTP_IP_ADDRESS, SITE_FTP_USERNAME, _sitePassword, ("/public_html" & destFolder) as string)
-- if a file and visible
if (visible of itemInfo) and not (folder of itemInfo) then
-- get last edited of list
set lastOploadedEdit to (GetEditDateOfFile_InList_(POSIX path of anItem, _loadedScript's l))
set lastGlobalEdit to (modification date of itemInfo) as date
-- if updated
if (lastOploadedEdit is missing value) or (lastOploadedEdit < lastGlobalEdit) then
-- construct upload URL
set uploadPath to ("ftp://" & SITE_FTP_ROOT_DIRECTORY) as string
repeat with i in componentsToAdd
set uploadPath to StringByAppendingPathComponent(uploadPath, i as string)
end repeat
set uploadPath to StringByAppendingPathComponent(uploadPath, name of itemInfo)
if _silenceMode is false then display dialog ("Uploading " & name of itemInfo & return & return & quoted form of (POSIX path of anItem)) as string buttons "OK" default button 1 giving up after 3
set uploadSuccess to (WebFunctions's uploadFile(POSIX path of anItem, uploadPath, SITE_FTP_USERNAME, _sitePassword))
if uploadSuccess then
LogToFile(quoted form of (POSIX path of anItem) & " successfully uploaded.")
SetEditDate_OfFile_(lastGlobalEdit, POSIX path of anItem)
SaveFileIndex(_loadedScript)
set _updatedCount to _updatedCount + 1
else
set _errorCount to _errorCount + 1
LogToFile("** ERROR **: " & quoted form of (POSIX path of anItem) & " could not be uploaded.")
end if
end if
end if
-- if a folder
if (folder of itemInfo) then
Main(anItem)
end if
end repeat
end Main
on CheckValidFolder(aHSF, prmpt)
try
get aHSF as alias
return aHSF as alias
on error
set newFolder to (choose folder with prompt prmpt)
return newFolder
end try
end CheckValidFolder
on LoadFileIndex()
load script FILE_INDEX_SCRIPT
return result
end LoadFileIndex
on SaveFileIndex(scr)
store script scr in FILE_INDEX_SCRIPT with replacing
end SaveFileIndex
on LoadWebFunctions()
set scriptFile to (path to me as text) & "Contents:Resources:Scripts:Web Functions.scpt"
set scriptFile to scriptFile as alias
return load script scriptFile
end LoadWebFunctions
on GetEditDateOfFile_InList_(fileName, fileList)
repeat with i in fileList
if (name of i) is fileName then
return date (contents of (contents of i))
end if
end repeat
return missing value
end GetEditDateOfFile_InList_
on SetEditDate_OfFile_(newDate, fileName)
repeat with i in (_loadedScript's l)
if name of i is fileName then
set contents of (contents of i) to (newDate as string)
return
end if
end repeat
set end of (_loadedScript's l) to {name:fileName, contents:(newDate as string)}
end SetEditDate_OfFile_
on CopyFile(sourceFile, destFolder)
do shell script "cp " & quoted form of (POSIX path of sourceFile) & space & quoted form of (POSIX path of destFolder)
end CopyFile
on PathComponents(fileName)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set components to every text item of fileName
set AppleScript's text item delimiters to ""
if fileName starts with "/" then set item 1 of components to "/"
-- if end with slash
if item -1 of components is "" then set components to (items 1 thru -2 of components)
set AppleScript's text item delimiters to tid
return components
end PathComponents
on PathWithComponents(array)
-- if empty
if (count array) is 0 then return ""
-- if slash
if (count array) is 1 and (item 1 of array) is "/" then return "/"
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set newPath to array as text
set AppleScript's text item delimiters to ""
if (item 1 of array) is "/" then set newPath to (characters 2 thru -1 of newPath) as string
set AppleScript's text item delimiters to tid
return newPath
end PathWithComponents
on StringByAppendingPathComponent(original, addition)
if original is not "" then if original does not end with "/" then set original to (original & "/") as string
set newString to (original & addition) as string
return newString
end StringByAppendingPathComponent
on FileExistsAtPath(aHSF)
try
get aHSF as alias
log "file \"" & aHSF & " exists"
return true
on error
log "file \"" & aHSF & " does not exist"
return false
end try
end FileExistsAtPath
on MakeFolder(aHSF)
try
do shell script "mkdir " & quoted form of (POSIX path of aHSF)
end try
return (aHSF as alias)
end MakeFolder
on LogToFile(errorString)
set myDate to do shell script "date \"+%d-%m-%Y || %H:%M:%S\""
set myData to (return & myDate & ": " & errorString) as string
if not FileExistsAtPath(LOG_FILE) then MakeFile(POSIX path of LOG_FILE, "1")
if FileExistsAtPath(LOG_FILE) then
set logf to LOG_FILE as alias
set OA to open for access logf with write permission
try
write myData to OA starting at eof
close access OA
on error
try
close access OA
end try
end try
end if
end LogToFile
on AskForPassword(prmpt)
set pass1 to text returned of (display dialog prmpt default answer "" with hidden answer)
set pass2 to text returned of (display dialog "Verify you password" default answer "" with hidden answer)
if pass1 is not pass2 then set pass1 to AskForPassword("The given passwords do not match")
return pass1
end AskForPassword
on stringForTerminal(fileName)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to " "
set allItems to every text item of fileName
set AppleScript's text item delimiters to "\\ "
set newPath to allItems as text
set AppleScript's text item delimiters to tid
return newPath
end stringForTerminal
on MakeFile(fileName, tsize)
do shell script "mkfile " & tsize & space & my stringForTerminal(fileName)
end MakeFile
Site Uploader.app/Contents/Resources/Scripts/web functions.scpt
on sourceOfWebPage(anURL)
set theSource to missing value
try
set theSource to do shell script "curl " & quoted form of anURL
end try
return theSource
end sourceOfWebPage
on MakeFolderOnFTPServer(serverAddress, sUsername, sPassword, dirCreation)
set allComponents to pathComponents(dirCreation)
repeat with i from 1 to (count allComponents)
set myPath to pathWithComponents(items 1 thru i of allComponents)
set myPath to stringForTerminal(myPath)
set myCommand to ("curl ftp://" & sUsername & ":" & sPassword & "@" & serverAddress) as string
set myCommand to (myCommand & " -Q \"MKD " & myPath & "\"") as string
try
do shell script myCommand
end try
end repeat
end MakeFolderOnFTPServer
on uploadFile(filename, anURL, userName, aPassword)
set anURL to my encodeTextForURL(anURL, false, false)
log anURL
set myName to "curl "
set myParam to "-T "
-- upload type
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "://"
try
set myType to text item 1 of anURL
set AppleScript's text item delimiters to tid
on error e
log "error 1: " & e
set AppleScript's text item delimiters to tid
return false
end try
-- upload path
try
set AppleScript's text item delimiters to "://"
set urlPath to text item 2 of anURL
set AppleScript's text item delimiters to tid
on error e
log "error 2: " & e
set AppleScript's text item delimiters to tid
return false
end try
-- full url
set AppleScript's text item delimiters to ""
set fullURLPath to (myType & "://" & userName & ":" & aPassword & "@" & urlPath) as text
set AppleScript's text item delimiters to tid
set fullCMD to (myName & myParam & quoted form of filename & " " & quoted form of fullURLPath) as string
-- upload
try
do shell script fullCMD
log fullCMD
return true
on error e
log "error 3: " & e
return false
end try
end uploadFile
on encodeTextForURL(this_text, encode_URL_A, encode_URL_B)
set the standard_characters to "abcdefghijklmnopqrstuvwxyz0123456789"
set the URL_A_chars to "$+!'/?;&@=#%><{}[]\"~`^\\|*"
set the URL_B_chars to ".-_:"
set the acceptable_characters to the standard_characters
if encode_URL_A is false then set the acceptable_characters to the acceptable_characters & the URL_A_chars
if encode_URL_B is false then set the acceptable_characters to the acceptable_characters & the URL_B_chars
set the encoded_text to ""
repeat with this_char in this_text
if this_char is in the acceptable_characters then
set the encoded_text to (the encoded_text & this_char)
else
set the encoded_text to (the encoded_text & encode_char(this_char)) as string
end if
end repeat
return the encoded_text
end encodeTextForURL
(* ****** === OTHER === ****** *)
on pathComponents(filename)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set components to every text item of filename
set AppleScript's text item delimiters to ""
if filename starts with "/" then set item 1 of components to "/"
-- if end with slash
if item -1 of components is "" then set components to (items 1 thru -2 of components)
set AppleScript's text item delimiters to tid
return components
end pathComponents
on stringForTerminal(filename)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to " "
set allItems to every text item of filename
set AppleScript's text item delimiters to "\\ "
set newPath to allItems as text
set AppleScript's text item delimiters to tid
return newPath
end stringForTerminal
on pathWithComponents(array)
-- if empty
if (count array) is 0 then return ""
-- if slash
if (count array) is 1 and (item 1 of array) is "/" then return "/"
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set newPath to array as text
set AppleScript's text item delimiters to ""
if (item 1 of array) is "/" then set newPath to (characters 2 thru -1 of newPath) as string
set AppleScript's text item delimiters to tid
return newPath
end pathWithComponents
Site Uploader.app/Contents/Resources/file-index.scpt
property l : {}
Those are all the scripts that are present in the application bundle. You can of course also save the script in a script bundle.
The only thing that bothers me is the time it takes to loop thru the entire contents of the HTML folder and then loop thru all stored modification dates. If someone knows a better way to loop thru the modification dates, please share it.
I hope this script can be useful to someone,
ief2