forked from pbowden-msft/Remove2011
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdockutil
667 lines (584 loc) · 29.7 KB
/
dockutil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
#!/usr/bin/python
# This file is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under
# which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft
# licenses the Third Party IP to you under the licensing terms for the Microsoft product. Microsoft reserves all other rights not expressly granted
# under this agreement, whether by implication, estoppel or otherwise.
# DOCKUTIL
# Copyright 2008 Kyle Crawford
# Provided for Informational Purposes Only
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
# IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
# See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
# Send bug reports and comments to kcrwfrd at gmail
# Possible future enhancements
# tie in with application identifier codes for locating apps and replacing them in the dock with newer versions?
import sys, plistlib, subprocess, os, getopt, re, pipes, tempfile, pwd
import platform
# default verbose printing to off
verbose = False
version = '2.0.2'
def usage(e=None):
"""Displays usage information and error if one occurred"""
print """usage: %(progname)s -h
usage: %(progname)s --add <path to item> | <url> [--label <label>] [ folder_options ] [ position_options ] [ plist_location_specification ] [--no-restart]
usage: %(progname)s --remove <dock item label> | all [ plist_location_specification ] [--no-restart]
usage: %(progname)s --move <dock item label> position_options [ plist_location_specification ]
usage: %(progname)s --find <dock item label> [ plist_location_specification ]
usage: %(progname)s --list [ plist_location_specification ]
usage: %(progname)s --version
position_options:
--replacing <dock item label name> replaces the item with the given dock label or adds the item to the end if item to replace is not found
--position [ index_number | beginning | end | middle ] inserts the item at a fixed position: can be an position by index number or keyword
--after <dock item label name> inserts the item immediately after the given dock label or at the end if the item is not found
--before <dock item label name> inserts the item immediately before the given dock label or at the end if the item is not found
--section [ apps | others ] specifies whether the item should be added to the apps or others section
plist_location_specifications:
<path to a specific plist> default is the dock plist for current user
<path to a home directory>
--allhomes attempts to locate all home directories and perform the operation on each of them
--homeloc overrides the default /Users location for home directories
folder_options:
--view [grid|fan|list|automatic] stack view option
--display [folder|stack] how to display a folder's icon
--sort [name|dateadded|datemodified|datecreated|kind] sets sorting option for a folder view
Examples:
The following adds TextEdit.app to the end of the current user's dock:
%(progname)s --add /Applications/TextEdit.app
The following replaces Time Machine with TextEdit.app in the current user's dock:
%(progname)s --add /Applications/TextEdit.app --replacing 'Time Machine'
The following adds TextEdit.app after the item Time Machine in every user's dock on that machine:
%(progname)s --add /Applications/TextEdit.app --after 'Time Machine' --allhomes
The following adds ~/Downloads as a grid stack displayed as a folder for every user's dock on that machine:
%(progname)s --add '~/Downloads' --view grid --display folder --allhomes
The following adds a url dock item after the Downloads dock item for every user's dock on that machine:
%(progname)s --add vnc://miniserver.local --label 'Mini VNC' --after Downloads --allhomes
The following removes System Preferences from every user's dock on that machine:
%(progname)s --remove 'System Preferences' --allhomes
The following moves System Preferences to the second slot on every user's dock on that machine:
%(progname)s --move 'System Preferences' --position 2 --allhomes
The following finds any instance of iTunes in the specified home directory's dock:
%(progname)s --find iTunes /Users/jsmith
The following lists all dock items for all home directories at homeloc in the form: item<tab>path<tab><section>tab<plist>
%(progname)s --list --homeloc /Volumes/RAID/Homes --allhomes
The following adds Firefox after Safari in the Default User Template without restarting the Dock
%(progname)s --add /Applications/Firefox.app --after Safari --no-restart '/System/Library/User Template/English.lproj'
Notes:
When specifying a relative path like ~/Documents with the --allhomes option, ~/Documents must be quoted like '~/Documents' to get the item relative to each home
Bugs:
Names containing special characters like accent marks will fail
Contact:
Send bug reports and comments to kcrwfrd at gmail.
""" % dict(progname = os.path.basename(sys.argv[0]))
if e:
print ""
print 'Error processing options:', e
sys.exit(1)
sys.exit(0)
def verboseOutput(*args):
"""Used by verbose option (-v) to send more output to stdout"""
if verbose:
try:
print "verbose:", args
except:
pass
def main():
"""Parses options and arguments and performs fuctions"""
# setup our getoput opts and args
try:
(optargs, args) = getopt.getopt(sys.argv[1:], 'hv', ["help", "version",
"section=", "list", "find=", "add=", "move=", "replacing=",
"remove=", "after=", "before=", "position=", "display=", "view=",
"sort=", "label=", "type=", "allhomes", "homeloc=", "no-restart", "hupdock="])
except getopt.GetoptError, e: # if parsing of options fails, display usage and parse error
usage(e)
# setup default values
global verbose
add_path = None
remove_labels = []
find_label = None
move_label = None
after_item = None
before_item = None
position = None
add_path = None
plist_path = None
list = False
all_homes = False
replace_label = None
section = None
displayas = None
showas = None
arrangement = None
tile_type = None
label_name = None
home_directories_loc = '/Users'
restart_dock = True
for opt, arg in optargs:
if opt in ("-h", "--help"):
usage()
elif opt == "-v":
verbose = True
elif opt == "--version":
print version
sys.exit(0)
elif opt == "--add":
add_path = arg
elif opt == "--replacing":
replace_label = arg
elif opt == "--move":
move_label = arg
elif opt == "--find":
find_label = arg
elif opt == "--remove":
remove_labels.append(arg)
elif opt == "--after":
after_item = arg
elif opt == "--before":
before_item = arg
elif opt == "--position":
position = arg
elif opt == "--label":
label_name = arg
elif opt == '--sort':
if arg == 'name':
arrangement = 1
elif arg == 'dateadded':
arrangement = 2
elif arg == 'datemodified':
arrangement = 3
elif arg == 'datecreated':
arrangement = 4
elif arg == 'kind':
arrangement = 5
else:
usage('unsupported --sort argument')
elif opt == '--view':
if arg == 'fan':
showas = 1
elif arg == 'grid':
showas = 2
elif arg == 'list':
showas = 3
elif arg == 'auto':
showas = 0
else:
usage('unsupported --view argument')
elif opt == '--display':
if arg == 'stack':
displayas = 0
elif arg == 'folder':
displayas = 1
else:
usage('unsupported --display argument')
elif opt == '--type':
tile_type = arg+'-tile'
elif opt == '--section':
section = 'persistent-'+arg
elif opt == '--list':
list = True
elif opt == '--allhomes':
all_homes = True
elif opt == '--homeloc':
home_directories_loc = arg
elif opt == '--no-restart':
restart_dock = False
# for legacy compatibility only
elif opt == '--hupdock':
if arg.lower() in ("false", "no", "off", "0"):
restart_dock = False
# check for an action
if add_path == None and not remove_labels and move_label == None and find_label == None and list == False:
usage('no action was specified')
# get the list of plists to process
# if allhomes option was set, get a list of home directories in the homedirectory location
if all_homes:
possible_homes = os.listdir(home_directories_loc)
plist_paths = [ home_directories_loc+'/'+home+'/Library/Preferences/com.apple.dock.plist' for home in possible_homes if os.path.exists(home_directories_loc+'/'+home+'/Library/Preferences/com.apple.dock.plist') and os.path.exists(home_directories_loc+'/'+home+'/Desktop')]
else: # allhomes was not specified
# if no plist argument, then use the user's home directory dock plist, otherwise use the arguments provided
if args == []:
plist_paths = [ os.path.expanduser('~/Library/Preferences/com.apple.dock.plist') ]
else:
plist_paths = args
# exit if we couldn't find any plists to process
if len(plist_paths) < 1:
print 'no dock plists were found'
sys.exit(1)
# loop over plist paths
for plist_path in plist_paths:
verboseOutput('processing', plist_path)
# a home directory is allowed as an argument, so if the plist_path is a
# directory, we append the relative path to the plist
if os.path.isdir(plist_path):
plist_path = os.path.join(plist_path,'Library/Preferences/com.apple.dock.plist')
# verify that the plist exists at the given path
# and expand and quote it for use when shelling out
if os.path.exists(os.path.expanduser(plist_path)):
plist_path = os.path.expanduser(plist_path)
plist_path = os.path.abspath(plist_path)
plist_path = pipes.quote(plist_path)
else:
print plist_path, 'does not seem to be a home directory or a dock plist'
sys.exit(1)
# check for each action and process accordingly
if remove_labels: # --remove action(s)
pl = readPlist(plist_path)
changed = False
for remove_label in remove_labels:
if removeItem(pl, remove_label):
changed = True
else:
print 'item', remove_label, 'was not found in', plist_path
if changed:
commitPlist(pl, plist_path, restart_dock)
elif list: # --list action
pl = readPlist(plist_path)
# print a tab separated line for each item in the plist
# for each section
for section in ['persistent-apps', 'persistent-others']:
# for item in section
for item in pl[section]:
try:
# join and print relevant data into a string separated by tabs
print '\t'.join((item['tile-data']['file-label'], item['tile-data']['file-data']['_CFURLString'], section, plist_path))
except:
pass
elif find_label != None: # --find action
# since we are only reading the plist, make a copy before converting it to be read
pl = readPlist(plist_path)
# set found state
item_found = False
# loop through dock items looking for a match with provided find_label
for section in ['persistent-apps', 'persistent-others']:
for item_offset in range(len(pl[section])):
try:
if pl[section][item_offset]['tile-data']['file-label'] == find_label:
item_found = True
print find_label, "was found in", section, "at slot", item_offset+1, "in", plist_path
except:
pass
if not item_found:
print find_label, "was not found in", plist_path
if not all_homes: # only exit non-zero if we aren't processing all homes, because for allhomes, exit status for find would be irrelevant
sys.exit(1)
elif move_label != None: # --move action
pl = readPlist(plist_path)
# check for a position option before processing
if position is None and before_item is None and after_item is None:
usage('move action requires a position destination')
# perform the move and save the plist if it was successful
if moveItem(pl, move_label, position, before_item, after_item):
commitPlist(pl, plist_path, restart_dock)
else:
print 'move failed for', move_label, 'in', plist_path
elif add_path != None: # --add action
if add_path.startswith('~'): # we've got a relative path and relative paths need to be processed by using a path relative to this home directory
real_add_path = re.sub('^~', plist_path.replace('/Library/Preferences/com.apple.dock.plist',''), add_path) # swap out the full home path for the ~
else:
real_add_path = add_path
# determine smart default values where possible
if section == None:
if real_add_path.endswith('.app') or real_add_path.endswith('.app/'): # we've got an application
section = 'persistent-apps'
elif displayas != None or showas != None or arrangement != None: # we've got a folder
section = 'persistent-others'
if tile_type is None: # if type was not specified, we try to figure that out using the filesystem
if os.path.isdir(real_add_path) and section != 'persistent-apps': # app bundles are directories too
tile_type = 'directory-tile'
elif re.match('\w*://', real_add_path): # regex to determine a url in the form xyz://abcdef.adsf.com/adsf
tile_type = 'url-tile'
section = 'persistent-others'
else:
tile_type = 'file-tile'
if section == None:
section = 'persistent-others'
if tile_type != 'url-tile': # paths can't be relative in dock items
real_add_path = os.path.realpath(real_add_path)
pl = readPlist(plist_path)
verboseOutput('adding', real_add_path)
# perform the add save the plist if it was successful
if addItem(pl, real_add_path, replace_label, position, before_item, after_item, section, displayas, showas, arrangement, tile_type, label_name):
commitPlist(pl, plist_path, restart_dock)
else:
print 'item', add_path, 'was not added to Dock'
if not all_homes: # only exit non-zero if we aren't processing all homes, because for allhomes, exit status for add would be irrelevant
sys.exit(1)
# NOTE on use of defaults
# We use defaults because it knows how to handle cfpreferences caching even when given a path rather than a domain
# This allows us to keep using path-based plist specifications rather than domains
# Preserving path based plists are important for people needing to run this on a non boot volume
# However if Apple stops using plists or moves the plist path, all of this will break
# So at that point we will have to change the API so users pass in a defaults domain or user rather than a plist path
def writePlist(pl, plist_path):
"""writes a plist object down to a file"""
# get the unescaped path
plist_path = path_as_string(plist_path)
# get a tempfile path for writing our plist
plist_import_path = tempfile.mktemp()
# Write the plist to our temporary plist for importing because defaults can't import from a pipe (yet)
plistlib.writePlist(pl, plist_import_path)
# get original permissions
plist_stat = os.stat(plist_path)
# If we are running as root, ensure we run as the correct user to update cfprefsd
if os.geteuid() == 0:
# Running defaults as the user only works if the user exists
if valid_uid(plist_stat.st_uid):
subprocess.Popen(['sudo', '-u', '#%d' % plist_stat.st_uid, '-g', '#%d' % plist_stat.st_gid, 'defaults', 'import', plist_path, plist_import_path])
else:
subprocess.Popen(['defaults', 'import', plist_path, plist_import_path])
os.chown(plist_path, plist_stat.st_uid, plist_stat.st_gid)
os.chmod(plist_path, plist_stat.st_mode)
else:
subprocess.Popen(['defaults', 'import', plist_path, plist_import_path])
def valid_uid(uid):
"""returns bool of whether uid can be resolved to a user"""
try:
pwd.getpwuid(uid)
return True
except:
return False
def getOsxVersion():
"""returns a tuple with the (major,minor,revision) numbers"""
# OS X Yosemite return 10.10, so we will be happy with len(...) == 2, then add 0 for last number
try:
mac_ver = tuple(int(n) for n in platform.mac_ver()[0].split('.'))
assert 2 <= len(mac_ver) <= 3
except Exception as e:
raise e
if len(mac_ver) == 2:
mac_ver = mac_ver + (0, )
return mac_ver
def readPlist(plist_path):
"""returns a plist object read from a file path"""
# get the unescaped path
plist_path = path_as_string(plist_path)
# get a tempfile path for exporting our defaults data
export_fifo = tempfile.mktemp()
# make a fifo for defaults export in a temp file
os.mkfifo(export_fifo)
# export to the fifo
osx_version = getOsxVersion()
if osx_version[1] >= 9:
subprocess.Popen(['defaults', 'export', plist_path, export_fifo]).communicate()
# convert the export to xml
plist_string = subprocess.Popen(['plutil', '-convert', 'xml1', export_fifo, '-o', '-'], stdout=subprocess.PIPE).stdout.read()
else:
try:
cmd = ['/usr/libexec/PlistBuddy','-x','-c', 'print',plist_path]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(plist_string,err) = proc.communicate()
except Exception as e:
raise e
# parse the xml into a dictionary
pl = plistlib.readPlistFromString(plist_string)
return pl
def path_as_string(path):
"""returns an unescaped string of the path"""
return subprocess.Popen('ls -d '+path, shell=True, stdout=subprocess.PIPE).stdout.read().rstrip('\n')
def moveItem(pl, move_label=None, position=None, before_item=None, after_item=None):
"""locates an existing dock item and moves it to a new position"""
for section in ['persistent-apps', 'persistent-others']:
item_to_move = None
# loop over the items looking for the item label
for item_offset in range(len(pl[section])):
if pl[section][item_offset]['tile-data']['file-label'] == move_label:
item_found = True
verboseOutput('found', move_label)
# make a copy of the found dock entry
item_to_move = pl[section][item_offset]
found_offset = item_offset
break
else:
verboseOutput('no match for', pl[section][item_offset]['tile-data']['file-label'])
# if the item wasn't found, continue to next section loop iteration
if item_to_move == None:
continue
# we are still inside the section for loop
# remove the found item
pl[section].remove(pl[section][item_offset])
# figure out where to re-insert the original dock item back into the plist
if position != None:
if position in [ 'beginning', 'begin', 'first' ]:
pl[section].insert(0, item_to_move)
return True
elif position in [ 'end', 'last' ]:
pl[section].append(item_to_move)
return True
elif position in [ 'middle', 'center' ]:
midpoint = int(len(pl[section])/2)
pl[section].insert(midpoint, item_to_move)
return True
else:
# if the position integer starts with a + or - , then add or subtract from its current position respectively
if position.startswith('-') or position.startswith('+'):
new_position = int(position) + found_offset
if new_position > len(pl[section]):
pl[section].append(item_to_move)
elif new_position < 0:
pl[section].insert(0, item_to_move)
else:
pl[section].insert(int(position) + found_offset, item_to_move)
return True
try:
int(position)
except:
print 'Invalid position', position
return False
pl[section].insert(int(position)-1, item_to_move)
return True
elif after_item != None or before_item != None:
# if after or before is set, find the offset of that item and do the insert relative to that offset
for item_offset in range(len(pl[section])):
try:
if after_item != None:
if pl[section][item_offset]['tile-data']['file-label'] == after_item:
pl[section].insert(item_offset+1, item_to_move)
return True
if before_item != None:
if pl[section][item_offset]['tile-data']['file-label'] == before_item:
pl[section].insert(item_offset, item_to_move)
return True
except KeyError:
pass
return False
def generate_guid():
"""returns guid string"""
return subprocess.Popen(['/usr/bin/uuidgen'],stdout=subprocess.PIPE).communicate()[0].rstrip()
def addItem(pl, add_path, replace_label=None, position=None, before_item=None, after_item=None, section='persistent-apps', displayas=1, showas=1, arrangement=2, tile_type='file-tile',label_name=None):
"""adds an item to an existing dock plist object"""
if displayas == None: displayas = 1
if showas == None: showas = 0
if arrangement == None: arrangement = 2
#fix problems with unicode file names
enc = (sys.stdin.encoding if sys.stdin.encoding else 'UTF-8')
add_path = unicode(add_path, enc)
# set a dock label if one isn't provided
if label_name == None:
if tile_type == 'url-tile':
label_name = add_path
section = 'persistent-others'
else:
base_name = re.sub('/$', '', add_path).split('/')[-1]
label_name = re.sub('.app$', '', base_name)
# only add if item label isn't already there
if replace_label != label_name:
for existing_dock_item in (pl[section]):
for label_key in ['file-label','label']:
if existing_dock_item['tile-data'].has_key(label_key):
if existing_dock_item['tile-data'][label_key] == label_name:
print "%s already exists in dock. Use --replacing '%s' to update an existing item" % (label_name, label_name)
return False
if replace_label != None:
for item_offset in range(len(pl[section])):
tile_replace_candidate = pl[section][item_offset]['tile-data']
if tile_replace_candidate[label_key_for_tile(tile_replace_candidate)] == replace_label:
verboseOutput('found', replace_label)
del pl[section][item_offset]
position = item_offset + 1
break
new_guid = generate_guid()
if tile_type == 'file-tile':
new_item = {'GUID': new_guid, 'tile-data': {'file-data': {'_CFURLString': add_path, '_CFURLStringType': 0},'file-label': label_name, 'file-type': 32}, 'tile-type': tile_type}
elif tile_type == 'directory-tile':
if subprocess.Popen(['/usr/bin/sw_vers', '-productVersion'],
stdout=subprocess.PIPE).stdout.read().rstrip().split('.')[1] == '4': # gets the decimal after 10 in sw_vers; 10.4 does not use 10.5 options for stacks
new_item = {'GUID': new_guid, 'tile-data': {'directory': 1, 'file-data': {'_CFURLString': add_path, '_CFURLStringType': 0}, 'file-label': label_name, 'file-type': 2 }, 'tile-type': tile_type}
else:
new_item = {'GUID': new_guid, 'tile-data': {'arrangement': arrangement, 'directory': 1, 'displayas': displayas, 'file-data': {'_CFURLString': add_path, '_CFURLStringType': 0}, 'file-label': label_name, 'file-type': 2, 'showas': showas}, 'tile-type': tile_type}
elif tile_type == 'url-tile':
new_item = {'GUID': new_guid, 'tile-data': {'label': label_name, 'url': {'_CFURLString': add_path, '_CFURLStringType': 15}}, 'tile-type': tile_type}
else:
print 'unknown type:', tile_type
sys.exit(1)
verboseOutput('adding', new_item)
if position != None:
if position in [ 'beginning', 'begin', 'first' ]:
pl[section].insert(0, new_item)
return True
elif position in [ 'end', 'last' ]:
pl[section].append(new_item)
return True
elif position in [ 'middle', 'center' ]:
midpoint = int(len(pl[section])/2)
pl[section].insert(midpoint, new_item)
return True
else:
try:
int(position)
except:
print 'Invalid position', position
return False
if int(position) == 0:
pl[section].insert(int(position), new_item)
elif int(position) > 0:
pl[section].insert(int(position)-1, new_item)
else:
pl[section].insert(int(position)+len(pl[section])+1, new_item)
return True
elif after_item != None or before_item !=None:
for item_offset in range(len(pl[section])):
try:
if after_item != None:
if pl[section][item_offset]['tile-data']['file-label'] == after_item:
pl[section].insert(item_offset+1, new_item)
return True
if before_item != None:
if pl[section][item_offset]['tile-data']['file-label'] == before_item:
pl[section].insert(item_offset, new_item)
return True
except KeyError:
pass
pl[section].append(new_item)
verboseOutput('item added at end')
return True
def removeItem(pl, item_name):
removal_succeeded = False
if item_name == "all":
verboseOutput('Removing all items')
pl['persistent-apps'] = []
pl['persistent-others'] = []
return True
for dock_item in pl['persistent-apps']:
if dock_item['tile-data'].get('file-data').get('_CFURLString') == item_name:
verboseOutput('found', item_name)
pl['persistent-apps'].remove(dock_item)
removal_succeeded = True
for dock_item in pl['persistent-others']:
if dock_item['tile-type'] == "url-tile":
if dock_item['tile-data'].get('label') == item_name:
verboseOutput('found', item_name)
pl['persistent-others'].remove(dock_item)
removal_succeeded = True
else:
if dock_item['tile-data'].get('file-label') == item_name:
verboseOutput('found', item_name)
pl['persistent-others'].remove(dock_item)
removal_succeeded = True
return removal_succeeded
def commitPlist(pl, plist_path, restart_dock):
writePlist(pl, plist_path)
if restart_dock:
os.system('/usr/bin/killall -HUP Dock >/dev/null 2>&1')
#def commitPlistLegacy(pl, plist_path, restart_dock):
# plist_string_path = path_as_string(plist_path)
# pl = removeLongs(pl)
# plist_stat = os.stat(plist_string_path)
# writePlist(pl, plist_path)
# convertPlist(plist_path, 'binary1')
# os.chown(plist_string_path, plist_stat.st_uid, plist_stat.st_gid)
# os.chmod(plist_string_path, plist_stat.st_mode)
# if restart_dock:
# os.system('/usr/bin/killall -HUP cfprefsd >/dev/null 2>&1')
# os.system('/usr/bin/killall -HUP Dock >/dev/null 2>&1')
#
def label_key_for_tile(item):
for label_key in ['file-label','label']:
if item.has_key(label_key):
return label_key
if __name__ == "__main__":
main()