Scripting Journler

Introduction

I wanted a simple, friendly GUI to a database of thoughts and ideas that was also a writing tool; in essence an easily searched, easily edited journal that would allow me to attach images and other media. I wanted this note manager/snippet collector to be scriptable (what else, coming from the editor of these pages?). I didn’t need a powerful (and expensive) database manager like DEVONThink; but I did want an elegant, inexpensive ($25, fully-functional demo), middle-of-the-road application for storing virtually anything and recovering it with a simple search from either its user interface or from a script.

There are lots of possibilities out there too. If you want simple lists, an outliner is enough - something like OmniOutliner will do the trick, and at the other extreme of sophistication as mentioned above, DEVONThink is for you. In between are Mori, which is scriptable; Caboodle, which is an unscriptable “snippet” collector; Yojimbo, which is Spotlight searchable and in its latest version now scriptable; SOHO Notes (an upgrade from its former version: StickyBrain), which I had used for years, but not scriptable (which is why I was looking); MacJournal, which seems to be aimed at blogging and podcasting but is not scriptable; and finally Journler, a relatively new mid-priced shareware app that’s under intense development by Phil Dow and seemed to me to have the features I wanted. 2.5.4 is the latest official version, but I’ve had considerable success with the Journler 2.5.5 betas which improve almost weekly (with an auto-update feature, so easy to keep them current).

MacWorld reviewer Jason Cranford Teague had this to say while giving it 4 ½ mice, and I quote a bit of his review:

In many ways, Journler is like iLife for your writing. Just as iTunes keeps track of songs and iPhoto keeps track of pictures, Journler keeps track of your documents. And just like the iLife applications, Journler includes the ability to create smart folders that will help you automatically keep documents together based on their subject and content. Journler can also pull in resources from other Apple programs. This not only includes photos from iPhoto or songs from iTunes, but also cards from Address Book and links from Safari. All of these resources can then be displayed directly in Journler (even Web pages) without having to open the originating application. You can even go beyond importing files from other applications: if you have the appropriate hardware connected to your computer, such as a microphone or camera, Journler can also record audio notes, take photographs, and capture video directly into an entry.
Journler offers a number of ways for assigning attributes to entries for search, recovery, and smart folder display purposes. A journal entry supports tags, categories, flags, a color label, a due date, and a title. All or combinations of these can be searched on, filtered for or collected in smart folders. Further, smart folders can be nested and the enclosed folders inherit the attributes of their parents; what Phil Dow, the developer, calls “Smart Families”. As mentioned in the quote above, an entry can contain web pages, pdfs, images, videos, iPod notes, and plain old rich text à la TextEdit. Last, but certainly not least, Journler entries are “Spotlight-able” - there is an mdimport daemon running in the background to keep a Spotlight metadata file current.

Journler is also blog savvy. It will export directly to several blog editiors like MarsEdit, for example. Journler will send an email using your default client and a Journal entry as the text. If the entry contains media and your client is the Mail.app, Journler attaches them to the email. (It wouldn’t be difficult to script that behavior for other email clients.) Not only can you email entries, you can copy existing emails from Mail into Journler. Jourler works with iWeb too. Select an entry, click the iWeb toolbar button or choose send to iWeb and you’ve created a web page media and all. A lot of horsepower here.

Deleting a smart folder doesn’t delete its contents, just the folder itself along with its attributes; entries that showed there remain in the Journal but no longer have that particular smart “home”. To make matters nicer from my point of view, Journler entries are stored in ~/Library/Application Support/Journler/Journler Entries/. Within that folder, each entry is itself a folder with a number: “Entry 345”, say. Within an entry folder two files always appear: a journler.jobj file that isn’t understandable, and a _Text.jrtfd file. If there are resources associated with the entry, there will be a Resources folder in the entry folder as well. The _Text.jrtfd file is actually a bundle and within it is an entry.rtfd file which when opened in TextEdit, looks exactly like the entry. Journler will export/import rtfd files with whatever media they contain, and it will also export them as html. When I migrated my original database from SOHO Notes (which is not scriptable and stores its data in a proprietary database) I just exported them all as rtf documents and re-imported them en masse into Journler. Voilá, I was up and running.

There’s no hidden database here; no confusing caches, no hidden files. Backing up your Journal is a simple matter of copying the ~/Library/Application Support/Journler/ folder in its entirety to your backup medium, and then copying the ~/Library/Preferences/com.phildow.journler.plist there as well if you want to preserve your preferences. I can still get mine on a 256M thumb drive with room to spare because I don’t have a lot of media files in my Journal. I back mine up using this script:


set tDisk to "ACB-TD" -- my thumb drive - use your own location here.
set D to do shell script "diskutil mount `diskutil list | grep " & tDisk & " | grep -o '[^ ]*$'`" -- notice the back ticks are not single quotes.
if paragraph 1 of D is "Disk Utility Tool" then
	display dialog "The specified thumb drive is not mounted." & return & return & "If it is plugged in, please unplug it and reinsert it in a free USB port." & return & return & "Otherwise, please plug it in." with title "Thumb Drive Not Found" with icon 0
	return
end if

-- Quit Journler if running -- prevent Journler activity during backup.
tell application "System Events" to if exists process "Journler" then tell application "Journler" to quit

-- set some paths and then use rsync
set Source to quoted form of POSIX path of alias ((path to application support folder from user domain as text) & "Journler:")
set Dest to "/Volumes/" & tDisk & "/Journler/"
set Prefs to alias ((path to preferences folder from user domain as text) & "com.phildow.journler.plist")

do shell script "rsync -aE --delete-after " & Source & space & Dest & " || echo -n"
tell application "Finder" to duplicate Prefs to alias tDisk with replacing
display dialog "Backup done - remember to dismount the thumb drive! with icon 0

I have only had one occasion to use this backup since I started keeping it when I managed to kill my Journal with an unfortunate AppleScript glitch, but Journler has got stuff in it that I definitely don’t want to lose, like all my software license keys, as just one example. At some point I’ll develop a launchd script following Craig Smith’s tutorial: Using launchd with AppleScript to Access a Flash Drive Automatically, so all I’ll have to do it plug my thumb drive in, get a Growl notification that it’s the right one, and leave it there until a second Growl notification tells me the backup is complete (too many projects, not enough focus - the lot of all retirees).

The Journler GUI

Figure 1 below (click on the thumbnail to see a larger image) shows the standard Journal Browser window (mine is actually substantially larger than the one shown).

Figure 1: Standard Journler Entry Browser Window (Click to Enlarge)

As can be seen in the image, the window is tabbed so you can have two entries open at once. Below the tab bar on the left is a calendar and then a sidebar of folders. The folder’s icon can be altered by right clicking the folder, selecting “Get Info” and proceeding as you would to change an icon in the Finder’s Get Info. Each of mine is a smart folder, so it collects journal entries that share any of its title, category, tags or content conditions. Two of my folders contain smart folders and these have conditions that collect a subset of the entries in the containing folder (one selected). The conditions of the containing folder are inherited by those it contains so only special specifiers need be added to the contained folder.

The right pane below the tab bar is a listing of all the entries in the selected folder. In this case, the Journal is selected, so all of my entries are shown. At the bottom right is a pane that shows containing folders and if their are linked resources in the displayed entry these appear here too under a separate bar. In this case, I have a link to another entry with more detail than the one shown. The middle bottom of the window is the selected entry, where the header Title, Date, Category, and Tags are all editable as is the content of the entry.

In the toolbar on the right is a search box (that will find anything), a filter button (looks like a sieve) that opens a filter pane that is useful in setting conditions for particular search sets, and finally, on the extreme right, a Lexicon icon. The Lexicon icon opens a new window in which all of the Spotlight content terms are listed alphabetically. Clicking on one in the left hand pane will show all the Journler entries in which it is found in the second column of the left hand pane and selecting any of those, will reveal the entry itself in the right-hand pane, and a new column of the search terms in that entry. Very useful alternative to the straight-forward Journal Browser search when the memory needs a bit of jogging.

Scripting Journler

Journler has a usefully complete AppleScript Dictionary, and with only one or two exeptional cases (for which there are alternatives), it works in a fairly straight-forward way. No real surprises here, thankfully.

Rather than repeat the “basics” here (in the interests of space here), I’ll point you at a page on the Journler website: Journler Features: AppleScript

One feature not mentioned in the description of Journler above is its Journler Drop Box. Found in ~/Library/Application Support/Journler/, this apparent folder is an instant way to get Finder objects into your Journal - anything dropped on this JDB will spawn a new window shown in the figure below:

Figure 2: Journler Drop Box Window

To the left of this window are all of your Journler Folders, and selecting any of these will give the entry you are creating that folder’s conditions. In addition, you can add Category and Tags preferences here as well. Shown in the box at the lower right is an image I have dropped on the the JDB, and the name of that image is editable. When you’re done, “Complete Import” moves that file to Journler from wherever it was (there is a preference to leave it where it was and copy it to Journler as well).

I use this as a primary means of getting images into Journler, and to make that easier, I have command-dragged the Journler Drop Box icon into the tool bar of the Finder window for ~/Library/Application Support/Journler/. It will thereafter appear in every Finder window, so it’s easy to drag a document to it and move it to your Journal from any Finder view. In addition, if you’re a Quicksilver user (I couldn’t get along without it - read about it in Wikipedia) it is possible to enter text directly from Quicksilver to the Journler Drop Box with an AppleScript linkage.

To use the script below, drop it (or a copy) into ~/Library/Application Support/Quicksilver/Actions/ and then quit and restart Quicksilver to get it registered. With that done, you can quickly jam anything you can type into Journler directly from a Quicksilver window: invoke QS; type a period “.” to get a text entry, type your entry text, phone number, whatever; tab to the second pane of the QS window, type the name you’ve given to the script (make it easy - mine is QJE), press return and the Journler Drop Box window will open. Do what you like, and you’ve a new entry. This can be made much more elaborate (to do away with the JDB window’s appearance), but I’ve kept it simple here.


property JDBx : ((path to application support from user domain as text) & "Journler:Journler Drop Box:")

using terms from application "Quicksilver"
	on process text inputString
		dropEntry(inputString)
	end process text
end using terms from

to dropEntry(someText)
	set tDate to (current date) as text
	tell (current date)
		tell ((its hours) * 100 + (its minutes)) as string to text 1 thru 2 & text 3 thru 4
		set timeStamp to result
	end tell
	set NE to open for access (JDBx & "QSNote-" & timeStamp) with write permission
	try
		set eof of NE to 0
		write someText to NE
		close access NE
	on error e
		close access NE
		display dialog "Quick Note Failed!" & return & "Error: " & e & return & "Script will Quit Now" with icon 0 buttons {"OK"} default button 1
		return
	end try
end dropEntry

One of the scripts given in the Journler AppleScripts mentioned earlier is this one for getting the selected text from a Journler entry:


tell application "Journler"
activate
tell application "System Events" to keystroke "c" using command down
set textSelection to the clipboard
end tell

Unfortunately, that rarely works for me for multiple selections (although it does for the first) for reasons I have not been able to determine. Instead, I use this which seems much more reliable:


tell application "Journler" to set E to first item of (get selected entries)
tell application "System Events" to tell text area 1 of scroll area 3 of window 1 of process "Journler" to if exists then
	set {x, y} to value of attribute "AXSelectedTextRange"
	if x is less than or equal to y then
		tell application "Journler" to set mySelection to (characters x thru y of E) as string
	else
		set mySelection to ""
	end if
end if
mySelection

I use the same approach for coloring or re-formatting text, etc.

One AppleScript not mentioned in the reference is the creation of a new Smart Folder. The reason for this is that one of the folder properties: conditions, says in its description

Figure 3: Journler Folder Properties (click to enlarge)

The “crash” alluded to is actually a Force Quit freeze, and Phil Dow says a fix for that will not be available until the Leopard version releases. For that reason (wanting to create a set of smart folders, some of them nested within the parent), I came up with this script which works for me, though your mileage may vary. You won’t lose anything by trying. A script of this complexity isn’t needed if you only want one or two conditions, by the way. See the Orphans script below for a simple way.


-- Types
property _tags : "keywords " -- note space
property _Category : "category "
property _Title : "title "
property _DateDue : "dateDueInt "
property _Blog : "blogged "
-- Negation/Assertion
property _Not : "not "
property _Y : "YES"
property _N : "NO"
-- Conditional
property _Contains : "contains[cd] '"
property _is : "matches[cd] '"
property _OnBefore : "<= "
property _Eq : "== "
property _Q : "'"
-- Particular values
-- you can set up variable values beforehand here and insert the text in the concatination
--
-- Conditions for containing smart folder (inherited by inners)
set OutCond to {_Category & _is & "Family" & _Q}
-- Conditions for interior folders
set InCond1 to {_tags & _Contains & "Judi" & _Q}
set InCond2 to {_tags & _Contains & "Adam" & _Q}
set InCond3 to {_tags & _Contains & "Roger" & _Q}
-- Make the folders
tell application "Journler"
	-- make the enclosing folder
	set cf to make new folder with properties {type:smart folder, conditions:(OutCond), name:"Family Stuff"}
	delay 0.5
	-- make children folders
	set nf1 to make new folder with properties {type:smart folder, conditions:(InCond1), name:"Judith Bell"}
	set nf2 to make new folder with properties {type:smart folder, conditions:(InCond2), name:"Adam Bell V"}
	set nf3 to make new folder with properties {type:smart folder, conditions:(InCond3), name:"Roger Sarty"}
	delay 0.5
	-- Add the inners to the outer
	add nf1 to cf
	add nf2 to cf
	add nf3 to cf
	save
end tell

If you intend to “play” with this script, the following is useful for determining what a particular set of conditions should look like and also for remembering what tags, conditions, and folder setups you have:


tell application "Journler"
	-- get tags and categories for existing smart folders
	set New to false
	set mySmartFolders to folders where its type is smart folder and its name is not "Journal" and its name is not "Trash"
	set myFolderCategories to {}
	set myFolderTags to {}
	set myCond to {}
	repeat with aFldr in mySmartFolders
		set Cond to conditions of aFldr -- this is a list, even if empty
		set end of myCond to Cond
		-- examine each item of the conditions found for categories and tags
		repeat with C in Cond -- go through the list's items
			if C contains "category" then
				tell last word of C to if it is not in myFolderCategories then set end of myFolderCategories to it -- keep the category if unique
			else if C contains "keywords" then
				tell last word of C to if it is not in myFolderTags then set end of myFolderTags to it
			end if
		end repeat
	end repeat
end tell
set tDoc to "My Journal Categories" & return & return
repeat with FC in myFolderCategories
	set tDoc to tDoc & FC & return
end repeat
set tDoc to tDoc & return & "My Journal Tags" & return & return
repeat with FT in myFolderTags
	set tDoc to tDoc & FT & return
end repeat
set tDoc to tDoc & return & "My Folder Conditions" & return & return
repeat with FCd in myCond
	set tDoc to tDoc & FCd & return
end repeat
-- This can written to a document or examined in the Results pane.

If you create a lot of entries as notes of something you’re working on, or if you decide to reorganize your smart folders, it’s easy to create a lot of orphan entries - entries that don’t have a smart folder in which they appear. All will be in the Journal folder, of course, but I like to find these once in a while. I use this:


tell application "Journler"
	-- some sauce
	set B to "OK"
	set myFldrs to name of folders where its name is not "Journal" and name is not "Trash"
	set Excl to choose from list myFldrs with title "Folder List" with prompt "   Choose all folders you want excluded from the survey." & return & return & "   (Hold the command key down to make multiple selections.)" with multiple selections allowed and empty selection allowed
	if Excl is not {} then -- remove the exclusions from myFldrs
		set LC to count myFldrs
		repeat with k from 1 to LC
			if item k of myFldrs is in Excl then set item k of myFldrs to missing value
		end repeat
		set myFldrs to myFldrs's every Unicode text -- does not include "missing value".
	end if
	-- At this point myFldrs no longer includes the excluded names.
	
	-- Check to see whether the Orphan script has been run before
	if "Orphans" is not in myFldrs then
		make new folder with properties {name:"Orphans", type:smart folder, conditions:{"category contains[cd] 'Orphans'" as Unicode text}}
	else
		set T to type of folder "Orphans"
		set C to (conditions of folder "Orphans") as string
		set Cat to (characters ((offset of "'" in C) + 1) thru -2 of C) as string
		set B to button returned of (display dialog "You have an 'Orphans' folder!" & return & return & "Its properties are:" & return & "Type: " & T & return & "Category: " & Cat & return & return & "Do you want to use that?" & return & return & "If not, you should Cancel and rename it." with icon 2)
	end if
	-- and now the meat
	if B is "OK" then
		set JE to entries of folder "Journal"
		repeat with E in JE
			if name of folder of E is not in myFldrs then set category of E to "Orphans" as Unicode text
		end repeat
		save
	end if
end tell

Journler has a very active forum to which the developer, Phil Dow, often responds. One section of that forum is Journler, AppleScript and Automator, definitely worth visiting if you are at all interested in scripting Journler. (I appear there as “NovaScotian”, my old username here, too).

I have written a number of scripts there. One of them is too long for those pages and I’ve stored it (along with the Orphans script above) in Scriptbuilders under the Journler Category and hope readers of this tutorial/review who take an interest in scripting Journler will contribute.

I’ve found Journler a real pleasure to deal with. Hope the readers of this article will give it a try - you’ll like it.