1 <?php
2 /**
3 * Contains Choose class for Alphred
4 *
5 * PHP version 5
6 *
7 * @package Alphred
8 * @copyright Shawn Patrick Rice 2014
9 * @license http://opensource.org/licenses/MIT MIT
10 * @version 1.0.0
11 * @author Shawn Patrick Rice <rice@shawnrice.org>
12 * @link http://www.github.com/shawnrice/alphred
13 * @link http://shawnrice.github.io/alphred
14 * @since File available since Release 1.0.0
15 *
16 */
17
18
19 namespace Alphred;
20
21 /**
22 * Creates and executes AppleScript "Choose from..." dialogs.
23 *
24 * The ones that aren't included can be better served by regular PHP scripting.
25 *
26 * @since 1.0.0
27 */
28 class Choose {
29
30 /**
31 * Creates and executes a "Choose from list" AppleScript dialog
32 *
33 * @since 1.0.0
34 * @todo Inure this to single-quote, double-quotes, and commas
35 *
36 * @param array $list An array of list items
37 * @param array|boolean $options An array of options to customize the dialog
38 * @return array an array of items chosen
39 */
40 public function from_list( $list, $options = false ) {
41 // Check is the list is an array. If not, return false, because, well, we need
42 // an array in order for this thing to work.
43 if ( ! is_array( $list ) ) {
44 return false;
45 }
46 // The osascript interpreter needs this to look like an AppleScript list, so
47 // convert the PHP array into an AppleScript list.
48 $list = '{"' . implode( '", "', $list ) . '"}';
49 // This is what the beginning of the AppleScript needs to look like
50 $start = "choose from list {$list}";
51 // An array of options. This is basically a translation table in that the keys
52 // are the AppleScript text, and the values are the options that can be passed
53 // to this function.
54 $default_options = [
55 'with title' => 'title',
56 'with prompt' => 'text',
57 'default items' => 'default',
58 'OK button name' => 'ok',
59 'cancel button name' => 'cancel',
60 'multiple selections allowed' => 'multiple',
61 'empty selection allowed' => 'empty'
62 ];
63 // Send the values to the AppleScript "Choose from..." factory to get some working
64 // AppleScript code.
65 $script = self::create( $start, $default_options, $options );
66
67 if ( $script ) {
68 // Run the script, process the output, and return what is usable
69 return self::process( exec( $script ) );
70 } else {
71 // The script was bad, so return false
72 return false;
73 }
74 }
75
76
77 /**
78 * Creates and executes a "Choose file(s)" AppleScript dialog
79 *
80 * @since 1.0.0
81 *
82 * @param array|boolean $options An array of options to customize the dialog
83 * @return array an array of file path(s)
84 */
85 public function file( $options = false ) {
86 // This is what the beginning of the AppleScript needs to look like
87 $start = 'choose file';
88 // An array of options. This is basically a translation table in that the keys
89 // are the AppleScript text, and the values are the options that can be passed
90 // to this function.
91 $default_options = [
92 'with prompt' => 'text',
93 'of type' => 'type',
94 'default location' => 'location',
95 'invisibles' => 'invisibles',
96 'multiple selections allowed' => 'multiple',
97 'showing package contents' => 'package_contents'
98 ];
99 // Send the values to the AppleScript "Choose from..." factory to get some working
100 // AppleScript code.
101 $script = self::create( $start, $default_options, $options );
102
103 if ( $script ) {
104 // Run the script, process the output, and return what is usable
105 return self::process( exec( $script ), 'alias ', true );
106 } else {
107 // The script was bad, so return false
108 return false;
109 }
110 }
111
112 /**
113 * Creates and executes a "Choose filename" AppleScript dialog
114 *
115 * @since 1.0.0
116 *
117 * @param array|boolean $options An array of options to customize the dialog
118 * @return array an array with the single item being a filename
119 */
120 public function filename( $options = false ) {
121 // This is what the beginning of the AppleScript needs to look like
122 $start = 'choose file name';
123 // An array of options. This is basically a translation table in that the keys
124 // are the AppleScript text, and the values are the options that can be passed
125 // to this function.
126 $default_options = [
127 'with prompt' => 'text',
128 'default name' => 'default',
129 'default location' => 'location'
130 ];
131 // Send the values to the AppleScript "Choose from..." factory to get some working
132 // AppleScript code.
133 $script = self::create( $start, $default_options, $options );
134
135 if ( $script ) {
136 // Run the script, process the output, and return what is usable
137 return self::process( exec( $script ), 'file ', true );
138 } else {
139 // The script was bad, so return false
140 return false;
141 }
142 }
143
144
145 /**
146 * Creates and executes a "Choose folder(s)" AppleScript dialog
147 *
148 * @since 1.0.0
149 *
150 * @param array|boolean $options An array of options to customize the dialog
151 * @return array an array of folder path(s)
152 */
153 public function folder( $options = false ) {
154 // This is what the beginning of the AppleScript needs to look like
155 $start = 'choose folder';
156 // An array of options. This is basically a translation table in that the keys
157 // are the AppleScript text, and the values are the options that can be passed
158 // to this function.
159 $default_options = [
160 'with prompt' => 'text',
161 'default location' => 'location',
162 'invisibles' => 'invisibles',
163 'multiple selections allowed' => 'multiple',
164 'showing package contents' => 'package_contents'
165 ];
166 // Send the values to the AppleScript "Choose from..." factory to get some working
167 // AppleScript code.
168 $script = self::create( $start, $default_options, $options );
169
170 if ( $script ) {
171 // Run the script, process the output, and return what is usable
172 return self::process( exec( $script ), 'alias ', true );
173 } else {
174 // The script was bad, so return false
175 return false;
176 }
177 }
178
179 /**
180 * Factory function to create "Choose from..." dialogs.
181 *
182 * This function will take a few arrays and magically create the AppleScript code necessary
183 * for osascript to run a "Choose from..." dialog.
184 *
185 * @since 1.0.0
186 *
187 * @param string $start the start of the script
188 * @param array $options all possible options
189 * @param array $selections the selected options
190 * @return string the bash command to run an osascript for a "Choose from..." dialog
191 */
192 private function create( $start, $options, $selections ) {
193
194 // The beginning of the script
195 $script = "osascript -e '{$start}";
196
197 // Cycle through the options and append to the $script variable
198 foreach ( $options as $key => $value ) :
199 // The quotes are on
200 $quotes = true;
201 if ( isset( $selections[ $value ] ) ) {
202 if ( is_array( $selections[ $value ] ) ) {
203 $selections[ $value ] = '{"' . implode( '", "', $selections[ $value ] ) . '"}';
204 $quotes = false;
205 }
206 if ( is_bool( $selections[ $value ] ) ) {
207 if ( $selections[ $value ] ) {
208 $selections[ $value ] = 'true';
209 } else {
210 $selections[ $value ] = 'false';
211 }
212 }
213 // Are the "quotes" open?
214 if ( $quotes ) {
215 $script .= " {$key} \"{$selections[ $value ]}\"";
216 } else {
217 $script .= " {$key} {$selections[ $value ]}";
218 }
219 }
220 endforeach;
221
222 // Return the script. Note that we're redirecting STDERR to STDOUT
223 return $script .= "' 2>&1";
224 }
225
226 /**
227 * Processes the returned text from an AppleScript interaction
228 *
229 * @since 1.0.0
230 *
231 * @param string $result the text that comes out of the AppleScript interaction
232 * @param mixed $strip text to strip out of the result
233 * @param boolean $path whether or not the result is a path
234 * @return array the processed response
235 */
236 private function process( $result, $strip = false, $path = false ) {
237 // Make sure the user didn't cancel the selection
238 if ( false !== strpos( $result, 'execution error: User canceled. (-128)' ) ) {
239 // User canceled the operation, so return an explicit "canceled"
240 return 'canceled';
241 }
242 // String out anything we need to strip out
243 if ( $strip ) {
244 $result = str_replace( $strip, '', $result );
245 }
246 // Are there commas in the response?
247 if ( false !== strpos( $result, ',' ) ) {
248 // There are commas, which means we are dealing with an array, so let's
249 // explode the values as necessary
250 $result = explode( ',', $result );
251 // Just trim everything
252 array_walk( $result, function( &$value, $key ) {
253 $value = trim( $value );
254 });
255 } else {
256 // Make the result an array for consistency
257 $result = [ $result ];
258 }
259 // Are we dealing with a path?
260 if ( $path ) {
261 // Change all dumb Apple paths to POSIX paths
262 array_walk( $result, function( &$value, $key ) {
263 $value = self::to_posix_path( $value );
264 });
265 }
266 // Return the usable result
267 return $result;
268 }
269
270 /**
271 * Converts to a POSIX path
272 *
273 * @since 1.0.0
274 *
275 * @param string $path the path
276 * @return string the path as POSIX
277 */
278 private function to_posix_path( $path ) {
279 // Basically, we just need to replace the semi-colons with forward slashses
280 return '/' . str_replace( ':', '/', $path );
281 }
282
283 }