├── .gitignore
├── LICENSE
├── README.md
├── package
├── DEVONthinkSearch.alfredworkflow
└── DEVONthinkSearch.json
└── src
├── icon.png
├── info.plist
├── search
├── setup
├── update.json
└── workflowHandler.sh
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
2 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
3 |
4 | If you want to use it in your own project, please link back to this GitHub repository.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DEVONthink Search Alfred 2 Workflow
2 | ==================================
3 |
4 | Features
5 | --------
6 |
7 | This workflow allows you to search your DEVONthink documents from within Alfred. It supports DEVONthink Personal as well as DEVONthink Pro (with multiple databases)
8 |
9 | Installation
10 | ------------
11 |
12 | [Download](http://bit.ly/18s4R1b) and import into Alfred 2.
13 |
14 | Usage
15 | -----
16 |
17 | ### Keywords
18 |
19 | * **`devon [your search string]`** - Search in all databases
20 |
21 | 
22 |
23 | * **`devondb [your search string]`** - Search in specific database
24 |
25 | 
26 | 
27 |
28 | * **`devondb!`** - Refresh database list
29 |
30 | The association between database names and uuids can only be obtained via AppleScript. This is done automatically if you run the `devondb` command for the first time. The information is then cached for better performance. If you add a new DEVONthink database later on, you'll have to run the `devondb!` command to refresh the database list.
31 |
32 | 
--------------------------------------------------------------------------------
/package/DEVONthinkSearch.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/markokaestner/devonthink-document-search/3314f8cb96eaf17ef7961b6b9060c2cc97550fe3/package/DEVONthinkSearch.alfredworkflow
--------------------------------------------------------------------------------
/package/DEVONthinkSearch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1.3,
3 | "download_url": "http://bit.ly/18s4R1b",
4 | "description": "bugfixes"
5 | }
--------------------------------------------------------------------------------
/src/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/markokaestner/devonthink-document-search/3314f8cb96eaf17ef7961b6b9060c2cc97550fe3/src/icon.png
--------------------------------------------------------------------------------
/src/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bundleid
6 | com.markokaestner.devonthinksearch
7 | connections
8 |
9 | 1220BFFD-3CB7-4BDF-BE34-127AEF70127C
10 |
11 | 1DDAB52E-A468-49A3-972A-0B249E1B9C0B
12 |
13 |
14 | destinationuid
15 | 1220BFFD-3CB7-4BDF-BE34-127AEF70127C
16 | modifiers
17 | 0
18 | modifiersubtext
19 |
20 |
21 |
22 | AEEE74EC-013E-4E75-9613-B680942F4B57
23 |
24 |
25 | destinationuid
26 | 30CB87BB-3A5B-4AFB-80FF-6049286B6513
27 | modifiers
28 | 0
29 | modifiersubtext
30 |
31 |
32 |
33 |
34 | createdby
35 | Marko Kaestner
36 | description
37 | Search DEVONthink Documents
38 | disabled
39 |
40 | name
41 | DEVONthink Search
42 | objects
43 |
44 |
45 | config
46 |
47 | type
48 | alfred.workflow.action.openfile
49 | uid
50 | 30CB87BB-3A5B-4AFB-80FF-6049286B6513
51 |
52 |
53 | config
54 |
55 | anchorfields
56 |
57 | daterange
58 | 0
59 | fields
60 |
61 |
62 | field
63 | kMDItemDisplayName
64 | not
65 |
66 | split
67 |
68 | value
69 | {query}
70 | words
71 |
72 |
73 |
74 | field
75 | kMDItemFinderComment
76 | not
77 |
78 | split
79 |
80 | value
81 | {query}
82 | words
83 |
84 |
85 |
86 | field
87 | kMDItemTextContent
88 | not
89 |
90 | split
91 |
92 | value
93 | {query}
94 | words
95 |
96 |
97 |
98 | field
99 | kMDItemTitle
100 | not
101 |
102 | split
103 |
104 | value
105 | {query}
106 | words
107 |
108 |
109 |
110 | includesystem
111 |
112 | keyword
113 | devon
114 | scopes
115 |
116 | title
117 | DEVONthink Search
118 | types
119 |
120 | com.devon-technologies.metadata.think
121 | com.devon-technologies.metadata.thinkpro
122 |
123 | withspace
124 |
125 |
126 | type
127 | alfred.workflow.input.filefilter
128 | uid
129 | AEEE74EC-013E-4E75-9613-B680942F4B57
130 |
131 |
132 | config
133 |
134 | argumenttype
135 | 0
136 | escaping
137 | 102
138 | keyword
139 | devondb
140 | script
141 | ./search "{query}" 2>&1 | tee devon.log
142 | title
143 | DEVONthink Search in Database
144 | type
145 | 0
146 | withspace
147 |
148 |
149 | type
150 | alfred.workflow.input.scriptfilter
151 | uid
152 | 1DDAB52E-A468-49A3-972A-0B249E1B9C0B
153 |
154 |
155 | config
156 |
157 | escaping
158 | 102
159 | script
160 | . workflowHandler.sh
161 |
162 | QUERY="{query}"
163 |
164 | if [ -f "$QUERY" ]; then
165 | open "$QUERY"
166 | else
167 | setPref "searchdb" "{query}" 0
168 | SEARCHTERM=$(getPref "searchterm" 0)
169 |
170 | /usr/bin/osascript -e "tell application \"Alfred 2\" to search \"devondb $SEARCHTERM\""
171 | fi
172 | type
173 | 0
174 |
175 | type
176 | alfred.workflow.action.script
177 | uid
178 | 1220BFFD-3CB7-4BDF-BE34-127AEF70127C
179 |
180 |
181 | config
182 |
183 | argumenttype
184 | 2
185 | escaping
186 | 127
187 | keyword
188 | devondb!
189 | runningsubtext
190 | Searching available databases
191 | script
192 | . workflowHandler.sh
193 | DATADIR=$(getDataDir)
194 | if [ ! -d "$DATADIR" ]; then
195 | mkdir -p "$DATADIR"
196 | fi
197 | ./setup > "${DATADIR}/databases"
198 | DBCOUNT=$(cat "${DATADIR}/databases" | wc -l)
199 | addResult "1" "" "DEVONthink database list updated" "$DBCOUNT databases found" "icon.png" "no"
200 | getXMLResults
201 | title
202 | Refresh DEVONthink Database List
203 | type
204 | 0
205 | withspace
206 |
207 |
208 | type
209 | alfred.workflow.input.scriptfilter
210 | uid
211 | 8F78B728-AEE3-4FDB-AC4D-57D4AEE5485D
212 |
213 |
214 | readme
215 |
216 | uidata
217 |
218 | 1220BFFD-3CB7-4BDF-BE34-127AEF70127C
219 |
220 | ypos
221 | 120
222 |
223 | 1DDAB52E-A468-49A3-972A-0B249E1B9C0B
224 |
225 | ypos
226 | 120
227 |
228 | 30CB87BB-3A5B-4AFB-80FF-6049286B6513
229 |
230 | ypos
231 | 10
232 |
233 | 8F78B728-AEE3-4FDB-AC4D-57D4AEE5485D
234 |
235 | ypos
236 | 230
237 |
238 | AEEE74EC-013E-4E75-9613-B680942F4B57
239 |
240 | ypos
241 | 10
242 |
243 |
244 | webaddress
245 | http://markokaestner.com
246 |
247 |
248 |
--------------------------------------------------------------------------------
/src/search:
--------------------------------------------------------------------------------
1 | . workflowHandler.sh
2 |
3 | QUERY="$1"
4 | UUID=$(date +"%s")
5 | DB=$(getPref "searchdb" 0)
6 | DATADIR="$(getDataDir)"
7 | DBFILE="${DATADIR}/databases"
8 |
9 | setupDatabases() {
10 | ./setup > "$DBFILE"
11 | }
12 |
13 | showDatabases() {
14 | addResult "${UUID}1" "all" "All databases" "" "icon.png" "yes"
15 | COUNT=1
16 | while read DB; do
17 | DBUUID=${DB%%|*}
18 | DBNAME=${DB##*|}
19 | addResult "${UUID}${COUNT}" "$DBUUID" "$DBNAME" "" "icon.png" "yes"
20 | let COUNT+=1
21 | done < "$DBFILE"
22 | }
23 |
24 | showResults() {
25 | OLDIFS="$IFS"
26 | IFS='
27 | '
28 |
29 | if [ "$DB" == "all" ]; then
30 | MATCHES=$(/usr/bin/mdfind "(kMDItemContentType == 'com.devon-technologies.metadata.think' || kMDItemContentType == 'com.devon-technologies.metadata.thinkpro') && (kMDItemDisplayName == '*${QUERY}*'cd || kMDItemFinderComment == '*${QUERY}*'cd || kMDItemTextContent == '*${QUERY}*'cd || kMDItemTitle == '*${QUERY}*'cd)")
31 | else
32 | MATCHES=$(/usr/bin/mdfind "(kMDItemContentType == 'com.devon-technologies.metadata.think' || kMDItemContentType == 'com.devon-technologies.metadata.thinkpro') && (kMDItemDisplayName == '*${QUERY}*'cd || kMDItemFinderComment == '*${QUERY}*'cd || kMDItemTextContent == '*${QUERY}*'cd || kMDItemTitle == '*${QUERY}*'cd)" | grep "$DB")
33 | fi
34 |
35 | COUNT=1
36 | for MATCH in ${MATCHES[*]}; do
37 | FILENAME=$(/usr/bin/mdls -name "kMDItemDisplayName" -raw $MATCH)
38 | DBUUID=$(echo "$MATCH" | rev | cut -d "/" -f 3 | rev)
39 | DBNAME=$(grep "$DBUUID" "$DBFILE" | cut -d "|" -f 2)
40 | addResult "${RANDOM}${COUNT}" "$MATCH" "$FILENAME" "$DBNAME" "icon.png" "yes"
41 | let COUNT+=1
42 | done
43 |
44 | IFS="$OLDIFS"
45 | }
46 |
47 | resetWorkflow() {
48 | setPref "searchterm" "" 0
49 | setPref "searchdb" "" 0
50 | }
51 |
52 | main() {
53 | if [ ! -d "$DATADIR" ]; then
54 | mkdir -p "$DATADIR"
55 | fi
56 |
57 | if [ ! -f "$DBFILE" ]; then
58 | setupDatabases
59 | fi
60 |
61 | if [ -z "$DB" ]; then
62 | setPref "searchterm" "$QUERY" 0
63 | showDatabases
64 | else
65 | showResults
66 | resetWorkflow
67 | fi
68 |
69 | getXMLResults
70 | }
71 |
72 | main
73 |
--------------------------------------------------------------------------------
/src/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/osascript
2 |
3 | on run argv
4 | try
5 | my getDTDBs()
6 | on error errmsg
7 | return errmsg
8 | end try
9 | end run
10 |
11 | on getDTDBs()
12 | set retVal to ""
13 | tell application "DEVONthink Pro"
14 | repeat with theDB in databases
15 | set dbUUID to uuid of theDB as string
16 | set dbName to name of theDB as string
17 | if (length of retVal) > 0 then
18 | set retVal to retVal & "\n"
19 | end if
20 | set retVal to retVal & dbUUID & "|" & dbName
21 | end repeat
22 | end tell
23 | return retVal
24 | end getDTDBs
25 |
--------------------------------------------------------------------------------
/src/update.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1.3,
3 | "remote_json": "https://raw.github.com/markokaestner/devonthink-document-search/master/package/DEVONthinkSearch.json"
4 | }
5 |
--------------------------------------------------------------------------------
/src/workflowHandler.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | VPREFS="${HOME}/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/"
4 | NVPREFS="${HOME}/Library/Application Support/Alfred 2/Workflow Data/"
5 |
6 | RESULTS=()
7 |
8 | ################################################################################
9 | # Adds a result to the result array
10 | #
11 | # $1 uid
12 | # $2 arg
13 | # $3 title
14 | # $4 subtitle
15 | # $5 icon
16 | # $6 valid
17 | # $7 autocomplete
18 | ###############################################################################
19 | addResult() {
20 | RESULT="- $(xmlEncode "$3")$(xmlEncode "$4")$(xmlEncode "$5")
"
21 | RESULTS+=("$RESULT")
22 | }
23 |
24 | ###############################################################################
25 | # Prints the feedback xml to stdout
26 | ###############################################################################
27 | getXMLResults() {
28 | echo ""
29 |
30 | # if [ "${#string[@]}" = "0" ]; then
31 | # echo "- No results foundPlease try another search term
"
32 | # fi
33 |
34 | for R in ${RESULTS[*]}; do
35 | echo "$R" | tr "\n" " "
36 | done
37 |
38 | echo ""
39 | }
40 |
41 | ###############################################################################
42 | # Escapes XML special characters with their entities
43 | ###############################################################################
44 | xmlEncode() {
45 | echo "$1" | sed s/"&"/"\&"/ | sed s/">"/"\>"/ | sed s/"<"/"\<"/ | sed s/"\'"/"\'"/ | sed s/"\""/"\""/
46 | }
47 |
48 | ###############################################################################
49 | # Read the bundleid from the workflow's info.plist
50 | ###############################################################################
51 | getBundleId() {
52 | /usr/libexec/PlistBuddy -c "Print :bundleid" "info.plist"
53 | }
54 |
55 | ###############################################################################
56 | # Get the workflow data dir
57 | ###############################################################################
58 | getDataDir() {
59 | local BUNDLEID=$(getBundleId)
60 | echo "${NVPREFS}${BUNDLEID}"
61 | }
62 |
63 | ###############################################################################
64 | # Get the workflow cache dri
65 | ###############################################################################
66 | getCacheDir() {
67 | local BUNDLEID=$(getBundleId)
68 | echo "${VPREFS}${BUNDLEID}"
69 | }
70 |
71 | ###############################################################################
72 | # Save key=value to the workflow properties
73 | #
74 | # $1 key
75 | # $2 value
76 | # $3 non-volatile 0/1
77 | # $4 filename (optional, filename will be "settings" if not specified)
78 | ###############################################################################
79 | setPref() {
80 | local BUNDLEID=$(getBundleId)
81 | if [ "$3" = "0" ]; then
82 | local PREFDIR="${VPREFS}${BUNDLEID}"
83 | else
84 | local PREFDIR="${NVPREFS}${BUNDLEID}"
85 | fi
86 |
87 | if [ ! -d "$PREFDIR" ]; then
88 | mkdir -p "$PREFDIR"
89 | fi
90 |
91 | if [ -z "$4" ]; then
92 | local PREFFILE="${PREFDIR}/settings"
93 | else
94 | local PREFFILE="${PREFDIR}/$4"
95 | fi
96 |
97 | if [ ! -f "$PREFFILE" ]; then
98 | touch "$PREFFILE"
99 | fi
100 |
101 | local KEY_EXISTS=$(grep -c "$1=" "$PREFFILE")
102 | if [ "$KEY_EXISTS" = "0" ]; then
103 | echo "$1=$2" >> "$PREFFILE"
104 | else
105 | sed -i "" s/"$1=.*"/"$1=$2"/ "$PREFFILE"
106 | fi
107 | }
108 |
109 | ###############################################################################
110 | # Read a value for a given key from the workflow preferences
111 | #
112 | # $1 key
113 | # $2 non-volatile 0/1
114 | # $3 filename (optional, filename will be "settings" if not specified)
115 | ###############################################################################
116 | getPref() {
117 | local BUNDLEID=$(getBundleId)
118 | if [ "$2" = "0" ]; then
119 | local PREFDIR="${VPREFS}${BUNDLEID}"
120 | else
121 | local PREFDIR="${NVPREFS}${BUNDLEID}"
122 | fi
123 |
124 | if [ ! -d "$PREFDIR" ]; then
125 | return
126 | fi
127 |
128 | if [ -z "$3" ]; then
129 | local PREFFILE="${PREFDIR}/settings"
130 | else
131 | local PREFFILE="${PREFDIR}/$3"
132 | fi
133 |
134 | if [ ! -f "$PREFFILE" ]; then
135 | return
136 | fi
137 |
138 | local VALUE=$(sed "/^\#/d" "$PREFFILE" | grep "$1" | tail -n 1 | cut -d "=" -f2-)
139 | echo "$VALUE"
140 | }
141 |
142 | getLang() {
143 | defaults read .GlobalPreferences AppleLanguages | tr -d [:space:] | cut -c2-3
144 | }
145 |
--------------------------------------------------------------------------------