Compare commits

..

176 Commits
v3.71 ... v3.72

Author SHA1 Message Date
Jay Lee
3090c59582 GAM 3.72 2016-10-12 14:19:07 -04:00
Jay Lee
1de7e32f25 fix error message and remove old comment 2016-10-12 14:10:22 -04:00
Jay Lee
2bf058b089 make sure courseId isn't already prefixed before prefixing 2016-10-12 13:34:59 -04:00
Jay Lee
c507714ab6 CrOS device actions (disable, deprovision, reenable...) 2016-10-12 11:56:31 -04:00
Jay Lee
cdc72d2f23 catch additional errors on guardian delete 2016-10-12 11:53:37 -04:00
Jay Lee
2368adfebd Update .gitignore to block MSI and WIX build files 2016-10-12 11:26:51 -04:00
Jay Lee
46c000f6d1 MSI Windows build 2016-10-12 11:25:26 -04:00
Jay Lee
4254ed4d86 x64 7zip, clean pyinsataller, python-64 path 2016-10-11 15:56:24 -04:00
Jay Lee
3233807c17 remove pathext from macos-gam.spec 2016-10-11 15:30:40 -04:00
DevicesMac
b3501a2d4a MacOS PyInstaller build files 2016-10-11 10:30:12 -04:00
Jay Lee
3d5ac7c448 windows-gam.spec rename 2016-10-11 09:48:20 -04:00
Jay Lee
11e2a3845d Rename gam.spec to windows-gam.spec 2016-10-11 09:47:32 -04:00
Jay Lee
9812c23513 Rename build.bat to windows-build.bat 2016-10-11 09:47:09 -04:00
Jay Lee
5c02c1b50c Merge pull request #266 from taers232c/master
Fix typo, clean up doPrintGroupMembers
2016-09-01 17:30:40 +00:00
Ross Scroggs
f3f3f91fcb Fix spacing 2016-09-01 10:10:19 -07:00
Ross Scroggs
f8be56efdc Fix type, clean up doPrintGroupMembers 2016-09-01 10:07:26 -07:00
Jay Lee
dcae0155b2 Simply and generalize print group-members so that fields like the new status are automatically included as Google updates API 2016-08-31 14:23:58 -04:00
Jay Lee
7c21c670dc Merge pull request #264 from taers232c/master
Add new fields to list of selectable fields in print groups
2016-08-31 12:59:19 +00:00
Ross Scroggs
44c0d4bf15 Add new fields to list of selectable fields in print groups 2016-08-30 19:05:51 -07:00
Jay Lee
39159d3ce7 Merge pull request #262 from taers232c/master
Make routine to get sendas/signature attributes
2016-08-31 01:59:29 +00:00
Ross Scroggs
62565d7c97 Update documentation 2016-08-30 16:48:28 -07:00
Ross Scroggs
b8b268f0ad default argument allowed for signature and sendas 2016-08-30 16:45:58 -07:00
Ross Scroggs
4182b3cd1e Print heading for info sendas 2016-08-30 12:08:37 -07:00
Ross Scroggs
24c0390174 Make routine to get sendas/signature attributes; allow treatasalias for gam signature 2016-08-30 11:52:27 -07:00
Jay Lee
2b336d8c9f Merge pull request #261 from taers232c/master
Cleanup documentation
2016-08-30 18:09:19 +00:00
Ross Scroggs
5b02a22d77 Cleanup documentation 2016-08-30 10:57:21 -07:00
Jay Lee
b680a6bf12 Merge pull request #260 from taers232c/master
Help for the Python challenged
2016-08-30 14:29:01 +00:00
Ross Scroggs
2b65d87bfa Help for the Python challenged 2016-08-30 07:23:52 -07:00
Jay Lee
e214ad8154 Merge pull request #259 from taers232c/master
Statement misplaced in doPrintCourseParticipants; correct documentation
2016-08-30 14:14:04 +00:00
Ross Scroggs
3bf2ecc6e3 Statement misplaced in doPrintCourseParticipants; correct documentation 2016-08-29 21:37:20 -07:00
Jay Lee
594e10d0a4 Merge pull request #258 from taers232c/master
Allow charset <CharSet> with gam batch, like gam csv
2016-08-29 20:03:03 +00:00
Ross Scroggs
656f87f89c Allow charset <CharSet> with gam batch, like gam csv 2016-08-29 12:46:03 -07:00
Jay Lee
a5dd9b69e3 Merge pull request #257 from taers232c/master
Handle orphaned Org Unit/Role Ids in print admins/adminroles
2016-08-29 17:18:28 +00:00
Ross Scroggs
ae8949c6d6 Handle orphaned OrgUnit/Role Ids 2016-08-27 08:39:42 -07:00
Ross Scroggs
eeb29355f1 Merge remote-tracking branch 'jay0lee/master' 2016-08-27 08:37:58 -07:00
Jay Lee
4dea7cb650 add issue template back 2016-08-27 11:35:27 -04:00
Jay Lee
da4177f0e4 Merge remote-tracking branch 'origin/master' into branch-3.63
Attempting to move branch-3.63 to master
2016-08-27 11:22:45 -04:00
Jay Lee
c7b44ae8d9 Merge pull request #255 from taers232c/branch-3.63
Update create datatransfer
2016-08-27 15:18:48 +00:00
Ross Scroggs
37726c493d Update create datatransfer
Allow drive as a synonym for Drive, avoids an API call
Update documentation
2016-08-27 05:50:59 -07:00
Jay Lee
83af5ee757 Merge pull request #250 from taers232c/branch-3.63
Clean up gam print guardians
2016-08-27 12:37:04 +00:00
Ross Scroggs
34b9029e90 Clean up print guardians
Make gam show guardians and gam print guardians
show guardians gives formatted output
print guardians gives CSV output
This is like all other print/show xxx commands.
2016-08-21 22:41:01 -07:00
Jay Lee
4596accf5a Merge pull request #212 from Yuyuu/master
Allow to search drive files to update by query
2016-04-03 12:41:20 -04:00
Vincent Tertre
0034704b3f Handle query for drive file update 2016-03-30 17:07:05 +02:00
Jay Lee
a334645910 ISSUE_TEMPLATE is shown in plaintext on new issues 2016-02-29 09:07:09 -05:00
Jay Lee
6519a5b007 First shot at an issue template
goal is to reduce # of "how do I" issues which should go to discussion
forum as well as bug reports which don't include full details.
2016-02-29 09:03:14 -05:00
Jay Lee
0aabe4ae9b Merge pull request #179 from taers232c/master
Optionally get admin email address from command line in doRequestOauth
2016-01-27 14:33:30 -05:00
Ross Scroggs
0a41b4ec68 Optionally get admin email address from command line in doRequestOauth 2016-01-27 10:41:17 -08:00
Ross Scroggs
9cf4a151aa Merge remote-tracking branch 'jay0lee/master' 2016-01-27 07:38:02 -08:00
Jay Lee
10fcf566b8 fix todrive for "gam print adminroles" 2016-01-27 09:32:48 -05:00
Jay Lee
2704a8b695 groups and mobile reports
gam report groups
gam report mobile  (always blank?)
2016-01-27 09:25:21 -05:00
Jay Lee
09814b7dcd Merge pull request #166 from taers232c/master
Dynamic scope repair/cleanup
2016-01-27 09:18:48 -05:00
Jay Lee
4f2ce2625d Merge pull request #176 from jeremi/fix_set_vacation
Setting up vacation is not working anymore
2016-01-27 09:15:28 -05:00
Ross Scroggs
9c368b7d10 Fix handling of nonexistent extra-args.txt 2016-01-22 07:25:01 -08:00
jeremi
f9bd5506c7 Setting up vacation was not working anymore
The underlying function enable as a UpdateVacation is exception a boolean and not a string.
2016-01-22 10:25:59 +08:00
Ross Scroggs
4a168d16a3 Get environment variables, signal files via table; Fix update groups to allow IDs 2016-01-15 14:28:19 -08:00
Ross Scroggs
b817bd04ec Handle "all users in domain" member in doPrintGroups
Get id in members list, use that if there is no email. If neither email
or id exist, give the “Not sure…” message
2016-01-14 15:29:57 -08:00
Ross Scroggs
43adae4e70 Eliminate superflous exception 2016-01-14 07:38:51 -08:00
Ross Scroggs
77ebba9c62 Drop temporary environment variable GAM_ADMIN
Admin email address comes from user via prompt, stored in gamscopes.json
2016-01-14 06:14:51 -08:00
Ross Scroggs
ee517c1800 Do better sort of API names in doRequestOAuth 2016-01-14 05:53:28 -08:00
Ross Scroggs
154099c3f4 Back to single scopes list 2016-01-13 21:32:51 -08:00
Ross Scroggs
1746845651 Handle Unicode in doGetNotifications 2016-01-13 08:07:44 -08:00
Ross Scroggs
d07ab2d7e1 Clean up string quotes 2016-01-12 19:33:11 -08:00
Ross Scroggs
f3b970ae14 getGDataOAuthToken (formerly tryOAuth) was wiping out additional_headers 2016-01-12 15:34:25 -08:00
Ross Scroggs
16d8cddf12 In gam print admins, include id: with orgUnitId in CSV file 2016-01-12 07:27:31 -08:00
Ross Scroggs
671f7d810c buildOrgUnitIdToNameMap only mapped top level org units 2016-01-11 23:14:24 -08:00
Ross Scroggs
0d94dd3fa5 Handle out of domain users better 2016-01-11 22:21:01 -08:00
Ross Scroggs
eb5cfde630 Handle JSON format errors 2016-01-11 10:06:53 -08:00
Ross Scroggs
aa04e3ec1d Fix select_default_scopes in doRequestOAuth
https://www.googleapis.com/auth/admin.directory.user.security is not a
subset of https://www.googleapis.com/auth/admin.directory.user
2016-01-11 08:35:01 -08:00
Ross Scroggs
0333e29eef Service creation, dynamic scope cleanup
Make routine getAPIversionHttpService to handle all steps to get a
service.
Make routine handleOAuthTokenError to handle OAuth token errors.
In doRequestOAuth, get admin email address from oauth2.txt if it
exists, otherwise prompt for it.
Move reading of gamscopes,json into SetGlobalVariables as a local
routine _getScopesAdminDomainFromGamScopesJson.
2016-01-10 09:58:21 -08:00
Ross Scroggs
8929ee534f Ease transition to new all service model
Get admin email address and domain from oauth2.txt if it exists
2016-01-07 13:07:43 -08:00
Ross Scroggs
8eb347488f Eliminate 'email' scope, may sure that selected_scopes has unique elements 2016-01-06 06:24:01 -08:00
Ross Scroggs
f8642a18df Delete 'email' scope
If this ‘email’ scope is included here then it better be output in the
scopes list in doRequestOAuth otherwise nothing works. Why was it even
here?
2016-01-05 12:06:18 -08:00
Ross Scroggs
12ccd58eae Fix error 2016-01-05 08:54:14 -08:00
Ross Scroggs
95bb288e38 Back to scopes by api 2016-01-05 08:18:19 -08:00
Ross Scroggs
5c64f0825f Fix UTF error in getDelegates 2016-01-04 13:10:25 -08:00
Ross Scroggs
25e97b97d4 Sort scopes in doRequestOauth, move scope over for readability 2016-01-04 12:57:23 -08:00
Ross Scroggs
d258d4da63 Refactor doRequestOAuth
The selected scopes list can't be created until completion; otherwise
turning off a scope in one API turns it off in all other APIs.

Elimination of child scopes is still supported in select-default.

Top level API list is sorted.
2016-01-04 12:23:54 -08:00
Ross Scroggs
cb79688a73 in doRequestOAuth, sort list by API title 2016-01-04 10:52:33 -08:00
Ross Scroggs
dae75f6234 Fix typo in email-audit-v1.jsom 2016-01-04 06:02:10 -08:00
Ross Scroggs
0209b51c4d Clean up no setCurrentAPIScopes, api, version not needed
No scopes error message changed to take API title
2016-01-04 05:48:07 -08:00
Ross Scroggs
f35c188496 Finish refactoring doRequestOAuth, make selected_scopes a set 2016-01-03 23:24:40 -08:00
Ross Scroggs
c00d820c75 Eliminate extraneous loop 2016-01-03 21:42:32 -08:00
Ross Scroggs
69689e286b Fix setCurrentAPIScopes to work for both buildGAPIObject and OAuthInfo 2016-01-03 20:39:34 -08:00
Ross Scroggs
5697580bd0 Fix typo 2016-01-03 20:21:33 -08:00
Ross Scroggs
2288e99e56 Dynamic scope cleanup 2016-01-03 20:19:40 -08:00
Ross Scroggs
c8fd6e76af Merge remote-tracking branch 'jay0lee/master' 2016-01-03 16:04:56 -08:00
Ross Scroggs
23cb9afec7 Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2016-01-03 16:04:44 -08:00
Jay Lee
20e8175ae1 further updates to allow scopes to be saved as list
also determine default scopes based on substrings
2016-01-03 14:10:20 -05:00
Ross Scroggs
3a38dceb5f Dynamic scope repair
The last changes broke GData services and gam oath info
2016-01-03 10:00:12 -08:00
Ross Scroggs
c885cdefbb Merge remote-tracking branch 'jay0lee/master' 2016-01-03 07:16:04 -08:00
Jay Lee
b7d5374718 refactor saving of scopes to be a single list, not per API
-allows for APIs which have overlapping scopes (Drive, Apps Activity)
-buildGAPIObject() can dynamically decide which scopes to ask for based
on:
-scopes that API claims to use (discovery document)
-scopes that user has prevously selected
-still need to fixup GData API calls to make do same.
2016-01-03 10:11:12 -05:00
Jay Lee
59a0aadd72 ignore gamscopes.json 2016-01-03 09:20:56 -05:00
Jay Lee
a3f218a98d Merge pull request #164 from taers232c/master
Dynamic scopes update, other cleanup
2016-01-03 09:18:36 -05:00
Ross Scroggs
0424ced649 Reorder cancel and continue/back in doRequestOAuth, add missing .keys() 2016-01-02 13:35:08 -08:00
Ross Scroggs
9f75968684 More clean up of doRequestOAuth 2016-01-02 12:38:45 -08:00
Ross Scroggs
90fd503838 If GA_DOMAIN not set, derive domain from GAM_ADMIN if defined
If neither set, we have to decide what to do.
2016-01-02 11:59:37 -08:00
Ross Scroggs
a7a3f2eef6 Clean up input handling in doRequestOauth 2016-01-02 09:52:10 -08:00
Ross Scroggs
008a65329e gam oauth info now verifies scopes 2016-01-02 09:10:22 -08:00
Ross Scroggs
2390c4284e Clearing cache_dir has to come after directory check 2016-01-02 07:44:48 -08:00
Ross Scroggs
75483185d6 Clean up
Clean up handling of missing json files
Clean up processing environment variables, signal files
2016-01-02 07:09:15 -08:00
Ross Scroggs
acb21cb926 Clean signature printing 2016-01-01 14:39:10 -08:00
Ross Scroggs
c0ee674060 Full scopes list causes internal error, back (for now) to API specific list 2016-01-01 14:04:01 -08:00
Ross Scroggs
7d69c8e3bd Dynamic scopes cleanup
I deleted the  'email' scope because it caused everything to fail as
it's not (and never was) authorized. What was/is it for?
2016-01-01 12:13:02 -08:00
Ross Scroggs
38a37c49de Merge remote-tracking branch 'jay0lee/master' 2016-01-01 08:11:01 -08:00
Jay Lee
a36478b1f5 Merge pull request #163 from taers232c/master
Dynamic scopes first steps
2016-01-01 11:09:56 -05:00
Ross Scroggs
bcb17cd0a5 in doRequestOauth, initialize API use_scopes from gamscopes.json if available 2016-01-01 07:40:22 -08:00
Ross Scroggs
1ec164a25a Bring gam oauth delete back, give creation message 2016-01-01 06:50:16 -08:00
Ross Scroggs
571a9dcb3e Handle no scopes for API
This should probably call doRequestOAuth rather than bailing out
2015-12-31 23:37:15 -08:00
Ross Scroggs
a0ac6265e9 If SetGlobalVariables calls doRequestOAuth, don't call again if command is oauth create 2015-12-31 23:18:44 -08:00
Ross Scroggs
ea6f49f7be Have OAuthInfo print API scope table 2015-12-31 22:54:07 -08:00
Ross Scroggs
0470680a4d Add some error checking for gamscopes.json 2015-12-31 20:43:33 -08:00
Ross Scroggs
e0c52c8660 First cut, dynamic scopes
Environment variable GAMSCOPESFILE points to scopes file.
Scopes file gamscopes.json
2015-12-31 20:20:38 -08:00
Ross Scroggs
3182ce031c Leave email scope in tryOAuth 2015-12-31 18:00:29 -08:00
Ross Scroggs
f6dd0ccd12 Cleanup doRequestOAuth
admin_email isn't used; If it's going to be used I'd say:
`
if GC-Values{GC_ADMIN):
  admin_email = GC_Values[GC_ADMIN]
 else:
  admin_email = raw_input(u'Please enter your admin email address: ')
`
'oauth2' isn't in API_VER_MAPPING so the remove fails. If it might go
back in but you don't want it here, say:
`
if u'oauth2' in apis:
  apis.remove(u'oauth2')
`
2015-12-31 17:30:39 -08:00
Ross Scroggs
775b0c8c60 Merge remote-tracking branch 'jay0lee/master' 2015-12-31 16:43:57 -08:00
Jay Lee
1c4424dd0b Get doRequestOAuth to actually print out list of scopes 2015-12-31 16:47:53 -05:00
Jay Lee
8501aec7bc gdata discovery file description updates 2015-12-31 12:47:03 -05:00
Jay Lee
05a36d3245 update Google OAuth URIs
upstream pull request is:
https://github.com/google/oauth2client/pull/368
2015-12-31 12:44:40 -05:00
Ross Scroggs
2bf8f9164e Fix typo, drop unneeded API from table 2015-12-31 08:59:38 -08:00
Ross Scroggs
56732ea3e8 Commit Jay's changes 2015-12-31 08:18:28 -08:00
Ross Scroggs
7a4b32aadb Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2015-12-31 08:10:35 -08:00
Jay Lee
16add1bf24 UBER_SCOPE for appsactivity-v1 API 2015-12-31 09:34:03 -05:00
Jay Lee
433cdfe87d define UBER_SCOPES to cut down on unnecessary scope overlap 2015-12-31 08:57:16 -05:00
Jay Lee
a3d0a0250a Remove usage of oauth2 API for now as it's nearly useless 2015-12-31 08:52:05 -05:00
Ross Scroggs
b037333d2b Clean up OAuthInfo 2015-12-31 01:01:05 -08:00
Ross Scroggs
bf6c2ef266 buildGAPIObject reworked
This gets everything working but does not address the issue of matching
the admins actual scope list.

You'd better define GA_DOMAIN as it used to come out of oauth2.txt if
it wasn't defined.

I had to add Audit API and Site Verification API to the service account
list of APIs and downloaded a new oauth2service.json.
2015-12-30 23:39:04 -08:00
Ross Scroggs
abde922b49 Only build object if necessary 2015-12-30 15:38:39 -08:00
Ross Scroggs
eca89ca5e9 Fix typo 2015-12-30 15:17:43 -08:00
Ross Scroggs
a91c987107 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 14:49:32 -08:00
Jay Lee
12166e2245 more work on dynamic scope selections 2015-12-30 17:10:06 -05:00
Ross Scroggs
e612c20141 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 13:44:32 -08:00
Jay Lee
d2039e5566 cleanup admin settings API name 2015-12-30 16:24:37 -05:00
Jay Lee
20bba75e41 default scope selections
basic logic is:
-if 1 scope for API, use it
-skip over scopes ending in .readonly, .action or .verify_only UNLESS
all scopes are readonly, use all scopes (this is case with reports api)
-all other scopes are used by default.
2015-12-30 16:12:49 -05:00
Ross Scroggs
2d26b647c8 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 12:54:40 -08:00
Ross Scroggs
ab0bec7a7b Merge remote-tracking branch 'jay0lee/master'
# Conflicts:
#	src/gam.py
2015-12-30 12:54:35 -08:00
Jay Lee
b6c5f1b1e7 gdata discovery files wrap into Windows build 2015-12-30 15:39:40 -05:00
Jay Lee
881cc4d255 redo of Ross' latest patch against my updates 2015-12-30 15:36:24 -05:00
Jay Lee
3d61973071 discovery files for 2 other gdata apis to standardize scope discovery 2015-12-30 15:30:58 -05:00
Ross Scroggs
5ae1f3c441 Cleanup 2015-12-30 11:50:17 -08:00
Ross Scroggs
6ae4cf495d Merge remote-tracking branch 'jay0lee/master' 2015-12-30 11:39:07 -08:00
Jay Lee
900fc9c4e3 sanitize some defaults 2015-12-30 14:22:33 -05:00
Ross Scroggs
0207e84551 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 11:11:11 -08:00
Jay Lee
3a2d663c86 Merge pull request #158 from taers232c/master
Fix typo, delete call to obsolete function
2015-12-30 14:10:25 -05:00
Ross Scroggs
7aaaaf9125 Fix type, delete obsolete function 2015-12-30 10:59:04 -08:00
Ross Scroggs
e051b9bffa Merge remote-tracking branch 'jay0lee/master' 2015-12-30 10:32:09 -08:00
Jay Lee
df0bcda952 Merge pull request #157 from taers232c/master
no_browser.txt still needed by output_csv
2015-12-30 13:28:49 -05:00
Jay Lee
d871378336 dynamically get API scopes for "gam oauth request" 2015-12-30 13:25:06 -05:00
Ross Scroggs
f99add7a3f no_browser.txt still needed by output_csv 2015-12-30 10:20:18 -08:00
Jay Lee
7515700b1a fix GC_Defaults dict init 2015-12-30 13:18:56 -05:00
Ross Scroggs
99db4d50d3 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 10:06:41 -08:00
Jay Lee
0cd8246bdb remove unused G+ APIs 2015-12-30 12:58:21 -05:00
Ross Scroggs
29ee81ef18 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 09:57:46 -08:00
Jay Lee
9e09c06770 gdata scopes as globals 2015-12-30 12:57:42 -05:00
Jay Lee
65603ca314 Merge pull request #156 from taers232c/master
Use GAM_ADMIN environment variable as short-term fix to hardcoded value
2015-12-30 12:56:50 -05:00
Ross Scroggs
a983d23f91 Define GAM_ADMIN environment variable 2015-12-30 09:52:18 -08:00
Ross Scroggs
3545306559 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 09:23:02 -08:00
Jay Lee
08163cc5cd remove some legacy variables 2015-12-30 12:13:57 -05:00
Jay Lee
c629c3424c TODOs for a few ugly hard coded hacks 2015-12-30 12:08:37 -05:00
Jay Lee
ef403119d9 Intial steps to switch to 100% service accounts
Still need to store admin email and granted scopes in a file as well as
build these on first run.
2015-12-30 11:30:03 -05:00
Ross Scroggs
798c126881 Merge remote-tracking branch 'jay0lee/master' 2015-12-30 06:43:08 -08:00
Jay Lee
813d503bb8 use object already avail instead of buildDiscoveryObject() 2015-12-30 09:15:34 -05:00
Jay Lee
7e71a06c5f Merge pull request #155 from taers232c/master
Cleanup and gam csv optimization
2015-12-30 09:02:20 -05:00
Ross Scroggs
68475a00c1 Merge remote-tracking branch 'origin/master' 2015-12-26 08:46:48 -08:00
Ross Scroggs
4d71b6943c Clean up, gam csv optimization
showCalSettings had nested loop over user
doLabel cleaned up, has starting point for parsing arguments passed in
from main
doUpdateUser has starting point for parsing arguments passed in from
main
gam csv processing is optimized. All '~~xxx~~' and '~xxx' substitutions
are found for each argument and loaded into a dictionary once before
the loop. During the loop, the substitutions can be performed quickly.
2015-12-26 08:46:42 -08:00
Ross Scroggs
42caddb8a3 Merge remote-tracking branch 'jay0lee/master' 2015-12-25 05:50:03 -08:00
Ross Scroggs
52d8604099 Merge remote-tracking branch 'jay0lee/master' 2015-12-25 05:48:41 -08:00
Jay Lee
2df3aef52d more fixes to doVacation 2015-12-25 05:42:47 -05:00
Jay Lee
9773e25932 Merge pull request #154 from taers232c/master
Fix doVacation bug
2015-12-25 05:37:45 -05:00
Ross Scroggs
cd766d90e4 Fix doVacation problem 2015-12-24 23:04:40 -08:00
Ross Scroggs
8f69fc84a8 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 12:49:36 -08:00
Jay Lee
a8f0882220 Merge pull request #153 from taers232c/master
Cleanup argument passing to callGAPI, callGAPIpages, callGData
2015-12-23 15:48:22 -05:00
Ross Scroggs
d79c28d2d3 Eliminate unneeded service=, function=, items= in calls to API functions
Saves 5.5kb!
2015-12-23 11:07:08 -08:00
Ross Scroggs
ba756d12b2 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 08:15:34 -08:00
Jay Lee
48e6872233 Merge pull request #150 from taers232c/master
Debugging only incompatible with gam batch - and gam csv -
2015-12-23 11:08:04 -05:00
Ross Scroggs
5037a9bbfd Debugging only incompatible with gam batch - and gam csv - 2015-12-23 07:35:36 -08:00
Ross Scroggs
a58e5e4276 Merge remote-tracking branch 'jay0lee/master' 2015-12-23 06:42:45 -08:00
14 changed files with 364 additions and 182 deletions

14
.github/ISSUE_TEMPLATE.txt vendored Normal file
View File

@@ -0,0 +1,14 @@
The issue tracker is for reporting product deficiencies. "How do I?" questions should be posted to the discussion forum at https://groups.google.com/group/google-apps-manager. When in doubt, start at the discussion forum and return here only when instructed to do so.
Please confirm the following:
* I have upgraded to the latest GAM release from https://git.io/gamreleases and I still have this issue.
* I am typing the command as described in the GAM Wiki at https://github.com/jay0lee/gam/wiki
Full steps to reproduce the issue:
1.
2.
3.
Expected outcome (what are you trying to do?):
Actual outcome (what errors or bad behavior do you see instead?):

3
src/.gitignore vendored
View File

@@ -67,3 +67,6 @@ gamcache/
gam/
gam-64/
*.zip
*.msi
*.wixobj
*.wixpdb

View File

@@ -16,7 +16,7 @@ If an item contains spaces, it should be surrounded by " or '.
<String> ::= a string of characters, surrounded by " or ' if it contains spaces
<TrueValues> ::= true|on|yes|enabled|1
<FalseValues>= false|off|no|disabled|0
<DataTransferService> ::= googleplus|google+|gplus|g+|googledrive|gdrive
<DataTransferService> ::= googleplus|google+|gplus|g+|googledrive|gdrive|drive
<ProductID> ::= Google-Apps|Google-Coordinate|Google-Drive-storage|Google-Vault
<SKUID> ::= apps|gafb|gafw|gams|gau|unlimited|d4w|dfw|coordinate|vault|vfe|
drive-20gb|drive20gb|20gb|drive-50gb|drive50gb|50gb|drive-200gb|drive200gb|200gb|drive-400gb|drive400gb|400gb|
@@ -27,7 +27,7 @@ If an item contains spaces, it should be surrounded by " or '.
# Basic items built from primatives
<Boolean> ::= <TrueValues>|<FalseValues>
<ByteCount> ::= <Number>[M|K|B]
<ByteCount> ::= <Number>[m|k|b]
<CIDRnetmask> ::= <Number>.<Number>.<Number>.<Number>/<Number>
<ColorHex> ::= #<Hex><Hex><Hex><Hex><Hex><Hex>
<DomainName> ::= <String>(.<String>)+
@@ -58,11 +58,9 @@ If an item contains spaces, it should be surrounded by " or '.
<CourseParticipantType> ::= teacher|teachers|student|students
<CrOSID> ::= <String>
<CrOSItem> ::= <CrOSID>|(query:<QueryCrOS>)|(query <QueryCrOS>)
<DestEmailAddress> ::= <EmailAddress>
<DomainAlias> ::= <String>
<DriveFileACLRole> :: =commenter|editor|owner|reader|writer
<EventColorIndex> ::== <Number in range 1-11>
<FieldName> ::= <String>
<DestEmailAddress> ::= <EmailAddress>
<DriveFileID> ::= <String>
<DriveFileURL> :: = https://docs.google.com/a/<DomainName>/document/d/<DriveFileID>/<String>
<DriveFileItem> ::= <DriveFileID>|<DriveFileURL>
@@ -70,6 +68,7 @@ If an item contains spaces, it should be surrounded by " or '.
<DriveFolderID> ::= <String>
<DriveFolderName> ::= <String>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<EventColorIndex> ::== <Number in range 1-11>
<EventID> ::= <String>
<FieldName> ::= <String>
<FileName> ::= <String>
@@ -90,9 +89,11 @@ If an item contains spaces, it should be surrounded by " or '.
<NotificationID> ::= <String>
<OrgUnitID> ::= <String>
<OrgUnitPath> ::= /|(/<String)+
<ParameterKey> ::= <String>
<ParameterValue> ::= <String>
<PermissionID> ::= id:<String>|<EmailAddress>|anyone|anyonewithlink
<PrinterID> ::= <String>
<PrintJobAge> ::= <Number>(m|h|d)
<PrintJobAge> ::= <Number>[m|h|d]
<PrintJobID> ::= <String>
<PrintJobStatus> ::= done|error|held|in_progress|queued|submitted
<PropertyKey> ::= <String>
@@ -201,24 +202,22 @@ If an item contains spaces, it should be surrounded by " or '.
<GroupFieldName> ::=
admincreated|
aliases|
description|
email|
id|
name
<GroupSettingsFieldName> ::=
allowexternalmembers|
allowgooglecommunication|
allowwebposting|
archiveonly|
customreplyto|
defaultmessagedenynotificationtext|
description|
email|
id|
includeinglobaladdresslist|gal|
isarchived|
maxmessagebytes|
memberscanpostasthegroup|
messagedisplayfont|
messagemoderationlevel|
name
primarylanguage|
replyto|
sendmessagedenynotification|
@@ -304,7 +303,6 @@ If an item contains spaces, it should be surrounded by " or '.
<FileFormatList> ::= '<FileFormat>(,<FileFormat)*'
<FilterIDList> ::= '<FilterID>(,<FilterID>)*'
<GroupFieldNameList> ::= '<GroupFieldName(,<GroupFieldName>)*'
<GroupSettingsFieldNameList> ::= '<GroupSettingsFieldName(,<GroupSettingsFieldName>)*'
<GroupList> ::= '<GroupItem>(,<GroupItem>)*'
<GuardianStateList> ::= '<GuardianState>(,<GuardianState>)*'
<LabelNameList> ::= '<LabelName>(,<LabelName)*'
@@ -387,10 +385,12 @@ If an item contains spaces, it should be surrounded by " or '.
(allowgooglecommunication <Boolean>)|
(allowwebposting <Boolean>)|
(archiveonly <Boolean>)|
(customfootertext <String>)|
(customreplyto <EmailAddress>)|
(defaultmessagedenynotificationtext <String>)|
(description <String>)|
(gal|includeInGlobalAddressList <Boolean>)|
(includecustomfooter <Boolean>)|
(isarchived <Boolean>)|
(maxmessagebytes <ByteCount>)|
(memberscanpostasthegroup <Boolean>)|
@@ -457,7 +457,7 @@ gam version
gam help
gam batch <FileName>|- [charset <Charset>]
gam csv <FileName>|- [charset <Charset>] [matchfield <FieldName> <RegularExpression>] gam <GAM argument list>
gam csv <FileName>|- [charset <Charset>] gam <GAM argument list>
gam oauth|oauth2 create|request
gam oauth|oauth2 delete|revoke
@@ -495,7 +495,7 @@ gam update customer [adminsecondaryemail|alternateemail <EmailAddress>] [languag
[address1|addressline1 <String>] [address2|addressline2 <String>] [address3|addressline3 <String>]
[locality <String>] [region <String>] [postalcode <String>] [country|countrycode <String>]
gam create datatransfer|transfer <OldOwnerID> <DataTransferService> <NewOwnerID>
gam create datatransfer|transfer <OldOwnerID> <DataTransferService> <NewOwnerID> (<ParameterKey> <ParameterValue>)*
gam info datatransfer|transfer <TransferID>
gam print datatransfers|transfers [todrive] [olduser|oldowner <UserItem>] [newuser|newowner <UserItem>] [status <String>]
@@ -585,7 +585,7 @@ gam update group <GroupItem> clear [owner] [manager] [member]
gam print groups [todrive] ([domain <DomainName>] [member <UserItem>])
[maxresults <Number>] [delimiter <String>]
[members] [owners] [managers] [settings] <GroupFieldName>* [fields <GroupFieldNameList>|<GroupSettingsFieldNameList>]
[members] [owners] [managers] [settings] <GroupFieldName>* [fields <GroupFieldNameList>]
gam print group-members|groups-members [todrive] ([domain <DomainName>] [member <UserItem>])|[group <GroupItem>]
@@ -643,11 +643,12 @@ gam course <CourseID> delete alias <CourseAlias>
gam course <CourseID> add teachers|students <UserItem>
gam course <CourseID> delete|remove teachers|students <UserItem>
gam course <CourseID> sync teachers|students <UserTypeEntity>
gam print course-participants [todrive](course <CourseID>)* [teacher] [student] [show all|students|teachers]
gam print course-participants [todrive] (course|class <CourseID>)*|([teacher <UserItem>] [student <UserItem>]) [show all|students|teachers]
gam create guardian|guardianinvite|inviteguardian <EmailAddress> <UserItem>
gam delete guardian|guardians <GuardianID <UserItem>
gam print guardian|guardians [nocsv] [todrive] [invitedguardian <GuardianID>] [student <UserItem>] [invitations] [states <GuardianStateList>] [<UserTypeEntity>]
gam delete guardian|guardians <GuardianID>|<EmailAddress> <UserItem>
gam show guardian|guardians [invitedguardian <GuardianID>] [student <UserItem>] [invitations] [states <GuardianStateList>] [<UserTypeEntity>]
gam print guardian|guardians [todrive] [invitedguardian <GuardianID>] [student <UserItem>] [invitations] [states <GuardianStateList>] [<UserTypeEntity>]
gam printer register
gam update printer <PrinterID> <PrinterAttributes>+
@@ -697,6 +698,7 @@ gam <UserTypeEntity> transfer seccals <UserItem> [keepuser]
gam <UserTypeEntity> print|show driveactivity [todrive] [fileid <DriveFileID>] [folderid <DriveFolderID>]
gam <UserTypeEntity> print|show drivesettings [todrive]
gam <UserTypeEntity> print|show filelist [todrive] [query <QueryDriveFile>] [allfields|<DriveFieldName>*]
gam <UserTypeEntity> show fileinfo <DriveFileID> [allfields|<DriveFieldName>*]
gam <UserTypeEntity> show filerevisions <DriveFileID>
gam <UserTypeEntity> show filetree
@@ -709,7 +711,6 @@ gam <UserTypeEntity> transfer drive <UserItem> [keepuser]
gam <UserTypeEntity> delete|del emptydrivefolders
gam <UserTypeEntity> empty drivetrash
gam <UserTypeEntity> add drivefileacl <DriveFileID> anyone|(user <UserItem>)|(group <GroupItem>)|(domain <DomainName>)
(role <DriveFileACLRole>) [withlink] [sendmail] [emailmessage <String>]
gam <UserTypeEntity> update drivefileacl <DriveFileID> <PermissionID>
@@ -794,8 +795,8 @@ gam <UserTypeEntity> language <Language>
gam <UserTypeEntity> pagesize 25|50|100
gam <UserTypeEntity> [add] sendas <EmailAddress> <Name> [replyto <EmailAddress>] [default] [treatasalias <Boolean>] [signature|sig <String>|(file <FileName> [charset <CharSet>]) (replace <RegularExpression> <String>)*]
gam <UserTypeEntity> update sendas <EmailAddress> [name <Name>] [replyto <EmailAddress>] [default] [treatasalias <Boolean>] [signature|sig <String>|(file <FileName> [charset <CharSet>]) (replace <RegularExpression> <String>)*]
gam <UserTypeEntity> [add] sendas <EmailAddress> <Name> [signature|sig <String>|(file <FileName> [charset <CharSet>]) (replace <RegularExpression> <String>)*] [replyto <EmailAddress>] [default] [treatasalias <Boolean>]
gam <UserTypeEntity> update sendas <EmailAddress> [name <Name>] [signature|sig <String>|(file <FileName> [charset <CharSet>]) (replace <RegularExpression> <String>)*] [replyto <EmailAddress>] [default] [treatasalias <Boolean>]
gam <UserTypeEntity> delete sendas <EmailAddress>
gam <UserTypeEntity> show sendas [format]
gam <UserTypeEntity> info sendas <EmailAddress> [format]
@@ -803,7 +804,7 @@ gam <UserTypeEntity> print sendas [todrive]
gam <UserTypeEntity> shortcuts <Boolean>
gam <UserTypeEntity> signature|sig <String>|(file <FileName> [charset <Charset>]) (replace <Tag> <String>)* [name <String>] [replyto <EmailAddress>]
gam <UserTypeEntity> signature|sig <String>|(file <FileName> [charset <Charset>]) (replace <Tag> <String>)* [name <String>] [replyto <EmailAddress>] [default] [treatasalias <Boolean>]
gam <UserTypeEntity> show signature|sig [format]
gam <UserTypeEntity> snippets <Boolean>

View File

@@ -1,17 +0,0 @@
rmdir /q /s gam
rmdir /q /s gam-64
rmdir /q /s build
rmdir /q /s dist
del /q /f gam-%1-windows.zip
del /q /f gam-%1-windows-x64.zip
c:\python27-32\scripts\pyinstaller -F --distpath=gam gam.spec
xcopy LICENSE gam\
xcopy whatsnew.txt gam\
del gam\w9xpopen.exe
"%ProgramFiles(x86)%\7-Zip\7z.exe" a -tzip gam-%1-windows.zip gam\ -xr!.svn
c:\python27\scripts\pyinstaller -F --distpath=gam-64 gam.spec
xcopy LICENSE gam-64\
xcopy whatsnew.txt gam-64\
"%ProgramFiles(x86)%\7-Zip\7z.exe" a -tzip gam-%1-windows-x64.zip gam-64\ -xr!.svn

View File

@@ -24,7 +24,7 @@ For more information, see http://git.io/gam
"""
__author__ = u'Jay Lee <jay0lee@gmail.com>'
__version__ = u'3.71'
__version__ = u'3.72'
__license__ = u'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
import sys, os, time, datetime, random, socket, csv, platform, re, base64, string, codecs, StringIO, subprocess, collections, mimetypes
@@ -1729,7 +1729,7 @@ def doDelCourseParticipant():
def doDelCourse():
croom = buildGAPIObject(u'classroom')
courseId = sys.argv[3]
if not courseId.isdigit():
if not courseId.isdigit() and courseid[:2] != u'd:':
courseId = u'd:%s' % courseId
callGAPI(croom.courses(), u'delete', id=courseId)
print u'Deleted Course %s' % courseId
@@ -1737,7 +1737,7 @@ def doDelCourse():
def doUpdateCourse():
croom = buildGAPIObject(u'classroom')
courseId = sys.argv[3]
if not courseId.isdigit():
if not courseId.isdigit() and courseid[:2] != u'd:':
courseId = u'd:%s' % courseId
body = {}
i = 4
@@ -2096,7 +2096,7 @@ def buildOrgUnitIdToNameMap():
def orgunit_from_orgunitid(orgunitid):
if not GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME]:
buildOrgUnitIdToNameMap()
return GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME][orgunitid]
return GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME].get(orgunitid, orgunitid)
def buildRoleIdToNameToIdMap():
cd = buildGAPIObject(u'directory')
@@ -2113,7 +2113,7 @@ def buildRoleIdToNameToIdMap():
def role_from_roleid(roleid):
if not GM_Globals[GM_MAP_ROLE_ID_TO_NAME]:
buildRoleIdToNameToIdMap()
return GM_Globals[GM_MAP_ROLE_ID_TO_NAME][roleid]
return GM_Globals[GM_MAP_ROLE_ID_TO_NAME].get(roleid, roleid)
def roleid_from_role(role):
if not GM_Globals[GM_MAP_ROLE_NAME_TO_ID]:
@@ -2155,6 +2155,7 @@ SERVICE_NAME_CHOICES_MAP = {
u'googleplus': u'Google+',
u'gplus': u'Google+',
u'g+': u'Google+',
u'drive': u'Drive',
u'googledrive': u'Drive',
u'gdrive': u'Drive',
}
@@ -2282,72 +2283,71 @@ def doGetDataTransferInfo():
print u' None'
print
def doPrintGuardians():
def doPrintShowGuardians(csvFormat):
croom = buildGAPIObject(u'classroom')
invitedEmailAddress = None
studentIds = [u'-',]
states = None
service = croom.userProfiles().guardians()
items = u'guardians'
guardians = []
show_csv = True
csvRows = []
todrive = False
titles = []
itemName = 'Guardians'
if csvFormat:
csvRows = []
todrive = False
titles = [u'studentEmail', u'studentId', u'invitedEmailAddress', u'guardianId']
i = 3
while i < len(sys.argv):
if sys.argv[i].lower() == u'invitedguardian':
invitedEmailAddress = sys.argv[i+1]
i += 2
elif sys.argv[i].lower() == u'nocsv':
show_csv = False
i += 1
elif sys.argv[i].lower() == u'todrive':
myarg = sys.argv[i].lower()
if csvFormat and myarg == u'todrive':
todrive = True
i += 1
elif sys.argv[i].lower() == u'student':
elif myarg == u'invitedguardian':
invitedEmailAddress = sys.argv[i+1]
i += 2
elif myarg == u'student':
studentIds = [sys.argv[i+1],]
i += 2
elif sys.argv[i].lower() == u'invitations':
elif myarg == u'invitations':
service = croom.userProfiles().guardianInvitations()
items = u'guardianInvitations'
itemName = 'Guardian Invitations'
titles = [u'studentEmail', u'studentId', u'invitedEmailAddress', u'invitationId']
if states == None:
states = [u'COMPLETE', u'PENDING', u'GUARDIAN_INVITATION_STATE_UNSPECIFIED']
i += 1
elif sys.argv[i].lower() == u'states':
elif myarg == u'states':
states = sys.argv[i+1].upper().replace(u',', u' ').split()
i += 2
elif sys.argv[i].lower() in usergroup_types:
studentIds = getUsersToModify(entity_type=sys.argv[i], entity=sys.argv[i+1])
elif myarg in usergroup_types:
studentIds = getUsersToModify(entity_type=myarg, entity=sys.argv[i+1])
i += 2
else:
print u'ERROR: %s is not a valid argument for "gam print guardians"' % sys.argv[i]
print u'ERROR: %s is not a valid argument for "gam %s guardians"' % (sys.argv[i], [u'show', u'print'][csvFormat])
sys.exit(2)
n = 1
i = 0
count = len(studentIds)
for studentId in studentIds:
i += 1
kwargs = {u'invitedEmailAddress': invitedEmailAddress, u'studentId': studentId}
if items == u'guardianInvitations':
kwargs[u'states'] = states
if studentId != u'-':
sys.stderr.write('\r')
sys.stderr.flush()
sys.stderr.write(u'Getting guardians for %s (%s/%s)%s' % (studentId, n, len(studentIds), u' ' * 40))
student_guardians = callGAPIpages(service, u'list', items=items, soft_errors=True, **kwargs)
# add student email to results since API does not return it
i = 0
while i < len(student_guardians):
student_guardians[i][u'studentEmail'] = studentId
i += 1
guardians = guardians + student_guardians
n += 1
sys.stderr.write(u'\n')
if show_csv:
for guardian in guardians:
addRowTitlesToCSVfile(flatten_json(guardian), csvRows, titles)
writeCSVfile(csvRows, titles, u'Guardians', todrive)
else:
for guardian in guardians:
print_json(None, guardian)
if csvFormat:
sys.stderr.write('\r')
sys.stderr.flush()
sys.stderr.write(u'Getting %s for %s%s%s' % (itemName, studentId, currentCount(i, count), u' ' * 40))
guardians = callGAPIpages(service, u'list', items=items, soft_errors=True, **kwargs)
if not csvFormat:
print u'Student: {0}, {1}:{2}'.format(studentId, itemName, currentCount(i, count))
for guardian in guardians:
print_json(None, guardian, spacing=u' ')
else:
for guardian in guardians:
guardian[u'studentEmail'] = studentId
addRowTitlesToCSVfile(flatten_json(guardian), csvRows, titles)
if csvFormat:
sys.stderr.write(u'\n')
writeCSVfile(csvRows, titles, itemName, todrive)
def doInviteGuardian():
croom = buildGAPIObject(u'classroom')
@@ -2361,7 +2361,7 @@ def doDeleteGuardian():
guardianId = sys.argv[3]
studentId = sys.argv[4]
try:
callGAPI(croom.userProfiles().guardians(), u'delete', throw_reasons=[u'notFound'], studentId=studentId, guardianId=guardianId)
callGAPI(croom.userProfiles().guardians(), u'delete', throw_reasons=[u'forbidden', u'notFound'], studentId=studentId, guardianId=guardianId)
print u'Deleted %s as a guardian of %s' % (guardianId, studentId)
except googleapiclient.errors.HttpError:
# See if there's a pending invitation
@@ -2424,7 +2424,7 @@ def doCreateCourse():
def doGetCourseInfo():
croom = buildGAPIObject(u'classroom')
courseId = sys.argv[3]
if not courseId.isdigit():
if not courseId.isdigit() and courseId[:2] != u'd:':
courseId = u'd:%s' % courseId
info = callGAPI(croom.courses(), u'get', id=courseId)
print_json(None, info)
@@ -2529,8 +2529,8 @@ def doPrintCourseParticipants():
else:
print u'ERROR: %s is not a valid argument for "gam print course-participants"' % sys.argv[i]
sys.exit(2)
sys.stderr.write(u'Retrieving courses for organization (may take some time for large accounts)...\n')
if len(courses) == 0:
sys.stderr.write(u'Retrieving courses for organization (may take some time for large accounts)...\n')
page_message = u'Got %%num_items%% courses...\n'
all_courses = callGAPIpages(croom.courses(), u'list', u'courses', page_message=page_message, teacherId=teacherId, studentId=studentId)
for course in all_courses:
@@ -4979,15 +4979,46 @@ def _processTags(tagReplacements, message):
message = re.sub(match.group(0), tagReplacements.get(match.group(1), u''), message)
return message
def getSendAsAttributes(i, myarg, body, tagReplacements, command):
if myarg == u'replace':
matchTag = getString(i+1, u'Tag')
matchReplacement = getString(i+2, u'String', emptyOK=True)
tagReplacements[matchTag] = matchReplacement
i += 3
elif myarg == u'name':
body[u'displayName'] = sys.argv[i+1]
i += 2
elif myarg == u'replyto':
body[u'replyToAddress'] = sys.argv[i+1]
i += 2
elif myarg == u'default':
body[u'isDefault'] = True
i += 1
elif myarg == u'treatasalias':
if sys.argv[i+1].lower() == u'true':
body[u'treatAsAlias'] = True
elif sys.argv[i+1].lower() == u'false':
body[u'treatAsAlias'] = False
else:
print u'ERROR: value for treatasalias must be true or false; got %s' % sys.argv[i+1]
sys.exit(2)
i += 2
else:
print u'ERROR: %s is not a valid argument for "gam <users> %s"' % (sys.argv[i], command)
sys.exit(2)
return i
def addUpdateSendAs(users, i, addCmd):
emailAddress = sys.argv[i]
if emailAddress.find(u'@') < 0:
emailAddress = emailAddress+u'@'+GC_Values[GC_DOMAIN]
i += 1
if addCmd:
command = [u'sendas', u'add sendas'][i == 6]
body = {u'sendAsEmail': emailAddress, u'displayName': sys.argv[i]}
i += 1
else:
command = u'update sendas'
body = {}
signature = None
tagReplacements = {}
@@ -5000,32 +5031,8 @@ def addUpdateSendAs(users, i, addCmd):
filename = sys.argv[i]
i, encoding = getCharSet(i+1)
signature = readFile(filename, encoding=encoding)
elif myarg == u'replace':
matchTag = getString(i+1, u'Tag')
matchReplacement = getString(i+2, u'String', emptyOK=True)
tagReplacements[matchTag] = matchReplacement
i += 3
elif myarg == u'treatasalias':
if sys.argv[i+1].lower() == u'true':
body[u'treatAsAlias'] = True
elif sys.argv[i+1].lower() == u'false':
body[u'treatAsAlias'] = False
else:
print u'ERROR: value for treatasalias must be true or false; got %s' % sys.argv[i+1]
sys.exit(2)
i += 2
elif myarg == u'default':
body[u'isDefault'] = True
i += 1
elif sys.argv[i].lower() == u'replyto':
body[u'replyToAddress'] = sys.argv[i+1]
i += 2
elif myarg == u'name':
body[u'displayName'] = sys.argv[i+1]
i += 2
else:
print u'ERROR: %s is not a valid argument for "gam <users> sendas"' % sys.argv[i]
sys.exit(2)
i = getSendAsAttributes(i, myarg, body, tagReplacements, command)
if signature != None:
if not signature:
body[u'signature'] = None
@@ -5135,6 +5142,7 @@ def infoSendAs(users):
user, gmail = buildGmailGAPIObject(user)
if not gmail:
continue
print u'User: {0}, Show SendAs Address:{1}'.format(user, currentCount(i, count))
result = callGAPI(gmail.users().settings().sendAs(), u'get',
soft_errors=True,
userId=u'me', sendAsEmail=emailAddress)
@@ -6213,20 +6221,7 @@ def doSignature(users):
body = {u'sendAsEmail': None}
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == u'replace':
matchTag = getString(i+1, u'Tag')
matchReplacement = getString(i+2, u'String', emptyOK=True)
tagReplacements[matchTag] = matchReplacement
i += 3
elif myarg == u'name':
body[u'displayName'] = sys.argv[i+1]
i += 2
elif myarg == u'replyto':
body[u'replyToAddress'] = sys.argv[i+1]
i += 2
else:
print u'ERROR: %s is not a valid argument for "gam <users> signature"' % sys.argv[i]
sys.exit(2)
i = getSendAsAttributes(i, myarg, body, tagReplacements, u'signature')
if tagReplacements:
body[u'signature'] = _processTags(tagReplacements, signature)
else:
@@ -7373,6 +7368,9 @@ def doUpdateCros():
devices = [deviceId,]
i = 4
body = {}
update_device = True
action_device = False
ack_wipe = False
while i < len(sys.argv):
if sys.argv[i].lower() == u'user':
body[u'annotatedUser'] = sys.argv[i + 1]
@@ -7383,15 +7381,32 @@ def doUpdateCros():
elif sys.argv[i].lower() == u'notes':
body[u'notes'] = sys.argv[i + 1]
i += 2
elif sys.argv[i].lower() == u'status':
body[u'status'] = sys.argv[i + 1].upper()
#if body[u'status'] not in [u'ACTIVE', u'DEPROVISIONED']:
# print u'ERROR: status must be active or deprovisioned; got %s' % body[u'status']
# sys.exit(2)
elif sys.argv[i].lower() == u'action':
update_device = False
action_device = True
action = sys.argv[i+1].replace(u'_', u'').replace(u'-', u'').lower()
deprovisionReason = None
if action in [u'deprovisionsamemodelreplace', u'deprovisionsamemodelreplacement']:
action = u'deprovision'
deprovisionReason = u'same_model_replacement'
elif action in [u'deprovisiondifferentmodelreplace', u'deprovisiondifferentmodelreplacement']:
action = u'deprovision'
deprovisionReason = u'differentmodelreplacement'
elif action in [u'deprovisionretiringdevice']:
action = u'deprovision'
deprovisionReason = u'retiring_device'
elif action not in [u'disable', u'reenable']:
print u'ERROR: expected action of deprovision_same_model_replace, deprovision_different_model_replace, deprovision_retiring_device, disable or reenable, got %s' % action
sys.exit(3)
body = {u'action': action}
if deprovisionReason:
body[u'deprovisionReason'] = deprovisionReason
i += 2
elif sys.argv[i].replace(u'_', u'').lower() in [u'acknowledgedevicetouchrequirement']:
ack_wipe = True
i += 1
elif sys.argv[i].lower() in [u'tag', u'asset', u'assetid']:
body[u'annotatedAssetId'] = sys.argv[i + 1]
#annotatedAssetId - Handle Asset Tag Field 2015-04-13
i += 2
elif sys.argv[i].lower() in [u'ou', u'org']:
body[u'orgUnitPath'] = sys.argv[i + 1]
@@ -7401,12 +7416,19 @@ def doUpdateCros():
else:
print u'ERROR: %s is not a valid argument for "gam update cros"' % sys.argv[i]
sys.exit(2)
i = 0
i = 1
device_count = len(devices)
for this_device in devices:
if update_device:
print u' updating %s (%s of %s)' % (this_device, i, device_count)
callGAPI(service=cd.chromeosdevices(), function=u'patch', deviceId=this_device, body=body, customerId=GC_Values[GC_CUSTOMER_ID])
elif action_device:
if body[u'action'] == u'deprovision' and not ack_wipe:
print u'WARNING: Refusing to deprovision %s because acknowledge_device_touch_requirement not specified. Deprovisioning a device means the device will have to be physically wiped and re-enrolled to be managed by your domain again. This requires physical access to the device and is very time consuming to perform for each device. Please add "acknowledge_device_touch_requirement" to the GAM command if you understand this and wish to proceed with the deprovision. Please also be aware that deprovisioning can have an effect on your device license count. See https://support.google.com/chrome/a/answer/3523633 for full details.' % (this_device)
sys.exit(3)
print u' performing action %s for %s (%s of %s)' % (action, this_device, i, device_count)
callGAPI(cd.chromeosdevices(), function=u'action', customerId=GC_Values[GC_CUSTOMER_ID], resourceId=this_device, body=body)
i += 1
print u' updating %s (%s/%s)' % (this_device, i, device_count)
callGAPI(cd.chromeosdevices(), u'patch', deviceId=this_device, body=body, customerId=GC_Values[GC_CUSTOMER_ID])
def doUpdateMobile():
cd = buildGAPIObject(u'directory')
@@ -8727,7 +8749,7 @@ def writeCSVfile(csvRows, titles, list_type, todrive):
else:
writer = csv.DictWriter(sys.stdout, fieldnames=titles, dialect=u'nixstdout', quoting=csv.QUOTE_MINIMAL)
try:
writer.writeheader()
writer.writerow(dict((item, item) for item in writer.fieldnames))
writer.writerows(csvRows)
except IOError as e:
systemErrorExit(6, e)
@@ -8984,9 +9006,11 @@ GROUP_ATTRIBUTES_ARGUMENT_TO_PROPERTY_MAP = {
u'allowgooglecommunication': u'allowGoogleCommunication',
u'allowwebposting': u'allowWebPosting',
u'archiveonly': u'archiveOnly',
u'customfootertext': u'customFooterText',
u'customreplyto': u'customReplyTo',
u'defaultmessagedenynotificationtext': u'defaultMessageDenyNotificationText',
u'gal': u'includeInGlobalAddressList',
u'includecustomfooter': u'includeCustomFooter',
u'includeinglobaladdresslist': u'includeInGlobalAddressList',
u'isarchived': u'isArchived',
u'maxmessagebytes': u'maxMessageBytes',
@@ -9265,17 +9289,17 @@ def doPrintAliases():
continue
writeCSVfile(csvRows, titles, u'Aliases', todrive)
MEMBERS_FIELD_NAMES = [u'group', u'id', u'email', u'role', u'type', u'name',]
def doPrintGroupMembers():
cd = buildGAPIObject(u'directory')
todrive = groupname = membernames = False
todrive = False
membernames = False
customer = GC_Values[GC_CUSTOMER_ID]
usedomain = usemember = None
fieldsList = []
titles = []
usedomain = None
usemember = None
fields = None
titles = [u'group']
csvRows = []
all_groups = []
groups_to_get = []
i = 3
while i < len(sys.argv):
if sys.argv[i].lower() == u'domain':
@@ -9290,57 +9314,42 @@ def doPrintGroupMembers():
customer = None
i += 2
elif sys.argv[i].lower() == u'fields':
fieldNameList = sys.argv[i+1].lower()
for field in fieldNameList.lower().replace(u',', u' ').split():
if field in MEMBERS_FIELD_NAMES:
fieldsList.append(field)
titles.append(field)
else:
print u'ERROR: field name must be one of %s; got %s' % (u', '.join(MEMBERS_FIELD_NAMES), field)
sys.exit(2)
memberFieldsList = sys.argv[i+1].replace(u',', u' ').lower().split()
fields = u'nextPageToken,members(%s)' % (','.join(memberFieldsList))
i += 2
elif sys.argv[i].lower() == u'membernames':
membernames = True
titles.append(u'name')
i += 1
elif sys.argv[i].lower() == u'group':
group_email = sys.argv[i+1].lower()
if group_email.find(u'@') == -1:
group_email = u'%s@%s' % (group_email, GC_Values[GC_DOMAIN])
all_groups = [{u'email': group_email}]
groups_to_get = [{u'email': group_email}]
i += 2
else:
print u'ERROR: %s is not a valid argument for "gam print group-members"' % sys.argv[i]
sys.exit(2)
if not fieldsList:
for field in [u'id', u'role', u'group', u'email', u'type']:
fieldsList.append(field)
titles.append(field)
if membernames:
titles.append(u'name')
else:
if u'name'in fieldsList:
membernames = True
fieldsList.remove(u'name')
if u'group' in fieldsList:
groupname = True
fieldsList.remove(u'group')
if not all_groups:
all_groups = callGAPIpages(cd.groups(), u'list', u'groups', message_attribute=u'email',
customer=customer, domain=usedomain, userKey=usemember, fields=u'nextPageToken,groups(email)')
if not groups_to_get:
groups_to_get = callGAPIpages(cd.groups(), u'list', u'groups', message_attribute=u'email',
customer=customer, domain=usedomain, userKey=usemember, fields=u'nextPageToken,groups(email)')
i = 0
count = len(all_groups)
for group in all_groups:
count = len(groups_to_get)
for group in groups_to_get:
i += 1
group_email = group[u'email']
sys.stderr.write(u'Getting members for %s (%s/%s)\n' % (group_email, i, count))
group_members = callGAPIpages(cd.members(), u'list', u'members', message_attribute=u'email', groupKey=group_email)
group_members = callGAPIpages(cd.members(), u'list', u'members',
message_attribute=u'email', groupKey=group_email, fields=fields)
for member in group_members:
member_attr = {}
if groupname:
member_attr[u'group'] = group_email
for title in fieldsList:
member_attr[title] = member[title]
if membernames:
for unwanted_item in [u'kind', u'etag']:
if unwanted_item in member:
del member[unwanted_item]
for title in member:
if title not in titles:
titles.append(title)
member[u'group'] = group_email
if membernames and u'type' in member and u'id' in member:
if member[u'type'] == u'USER':
try:
mbinfo = callGAPI(cd.users(), u'get',
@@ -9359,8 +9368,8 @@ def doPrintGroupMembers():
memberName = u'Unknown'
else:
memberName = u'Unknown'
member_attr[u'name'] = memberName
csvRows.append(member_attr)
member[u'name'] = memberName
csvRows.append(member)
writeCSVfile(csvRows, titles, u'Group Members', todrive)
def doPrintMobileDevices():
@@ -10534,9 +10543,13 @@ def ProcessGAMCommand(args):
command = sys.argv[1].lower()
if command == u'batch':
import shlex
f = openFile(sys.argv[2])
i = 2
filename = sys.argv[i]
i, encoding = getCharSet(i+1)
f = openFile(filename)
batchFile = UTF8Recoder(f, encoding) if encoding != u'utf-8' else f
items = []
for line in f:
for line in batchFile:
argv = shlex.split(line)
if not argv:
continue
@@ -10817,7 +10830,7 @@ def ProcessGAMCommand(args):
elif argument in [u'roles', u'adminroles']:
doPrintAdminRoles()
elif argument in [u'guardian', u'guardians']:
doPrintGuardians()
doPrintShowGuardians(True)
else:
print u'ERROR: %s is not a valid argument for "gam print"' % argument
sys.exit(2)
@@ -10826,6 +10839,8 @@ def ProcessGAMCommand(args):
argument = sys.argv[2].lower()
if argument in [u'schema', u'schemas']:
doPrintShowUserSchemas(False)
elif argument in [u'guardian', u'guardians']:
doPrintShowGuardians(False)
else:
print u'ERROR: %s is not a valid argument for "gam show"' % argument
sys.exit(2)

64
src/gam.wxs Normal file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" >
<Product
Id="*"
Name="GAM"
Language="1033"
Version="$(env.GAMVERSION)"
Manufacturer="Jay Lee - jay0lee@gmail.com"
UpgradeCode="15C5FD61-B04C-4E04-A26D-CD8424C19D9F">
<Package
InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade
DowngradeErrorMessage=
"A newer version of [ProductName] is already installed."
Schedule="afterInstallExecute" />
<MediaTemplate EmbedCab="yes" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
<UIRef Id="WixUI_InstallDir" />
<Feature
Id="gam"
Title="GAM"
Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ROOTDRIVE">
<Directory Id="INSTALLFOLDER" Name="GAM" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<!-- Group of components that are our main application items -->
<ComponentGroup
Id="ProductComponents"
Directory="INSTALLFOLDER"
Source="gam-64">
<Component Id="gam_exe" Guid="886abc07-73c5-4acc-9f71-58daf62aabc1">
<File Name="gam.exe" KeyPath="yes" />
</Component>
<Component Id="license" Guid="7a15de2e-fb91-4d0a-b8bf-c8b19c68f569">
<File Name="LICENSE" KeyPath="yes" />
</Component>
<Component Id="whatsnew_txt" Guid="6aa9863c-90d9-412f-9b73-fda82549a950">
<File Name="whatsnew.txt" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<InstallUISequence>
<ExecuteAction />
<Show Dialog="WelcomeDlg" Before="ProgressDlg" />
<!-- <Show Dialog="ProgressDlg" After="" /> -->
</InstallUISequence>
</Fragment>
</Wix>

BIN
src/license.rtf Normal file

Binary file not shown.

10
src/linux-build.sh Executable file
View File

@@ -0,0 +1,10 @@
rm -rf gam
rm -rf build
rm -rf dist
rm -rf gam-$1-linux-$(arch).tar.xz
pyinstaller --clean -F --distpath=gam linux-gam.spec
cp LICENSE gam
cp whatsnew.txt gam
tar cfJ gam-$1-linux-$(arch).tar.xz gam/

26
src/linux-gam.spec Normal file
View File

@@ -0,0 +1,26 @@
# -*- mode: python -*-
a = Analysis(['gam.py'],
hiddenimports=[],
hookspath=None,
excludes=['_tkinter'],
runtime_hooks=None)
for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
a.datas += [('httplib2/cacerts.txt', 'httplib2\cacerts.txt', 'DATA')]
a.datas += [('admin-settings-v2.json', 'admin-settings-v2.json', 'DATA')]
a.datas += [('cloudprint-v2.json', 'cloudprint-v2.json', 'DATA')]
a.datas += [('email-audit-v1.json', 'email-audit-v1.json', 'DATA')]
a.datas += [('email-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='gam',
debug=False,
strip=None,
upx=True,
console=True )

10
src/macos-build.sh Executable file
View File

@@ -0,0 +1,10 @@
rmdir /q /s gam
rmdir /q /s build
rmdir /q /s dist
rm -rf gam-$1-macos.tar.xz
pyinstaller --clean -F --distpath=gam macos-gam.spec
cp LICENSE gam
cp whatsnew.txt gam
tar cfJ gam-$1-macos.tar.xz gam/

26
src/macos-gam.spec Normal file
View File

@@ -0,0 +1,26 @@
# -*- mode: python -*-
a = Analysis(['gam.py'],
hiddenimports=[],
hookspath=None,
excludes=['_tkinter'],
runtime_hooks=None)
for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
a.datas += [('httplib2/cacerts.txt', 'httplib2\cacerts.txt', 'DATA')]
a.datas += [('admin-settings-v2.json', 'admin-settings-v2.json', 'DATA')]
a.datas += [('cloudprint-v2.json', 'cloudprint-v2.json', 'DATA')]
a.datas += [('email-audit-v1.json', 'email-audit-v1.json', 'DATA')]
a.datas += [('email-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='gam',
debug=False,
strip=None,
upx=True,
console=True )

View File

@@ -1,3 +1,9 @@
GAM 3.72
- Chrome OS device actions, disable, re-enable and deprovision devices.
- (beta) MSI Windows build
- (beta) binary Linux and MacOS builds
- Numerous fixes and updates by Ross
GAM 3.71
- Fix update first / last name.
- upgrade GAM versions of oauth2client, googleapiclient, RSA and six

24
src/windows-build.bat Normal file
View File

@@ -0,0 +1,24 @@
rmdir /q /s gam
rmdir /q /s gam-64
rmdir /q /s build
rmdir /q /s dist
del /q /f gam-%1-windows.zip
del /q /f gam-%1-windows-x64.zip
del /q /f gam-%1-windows-x64.msi
del /q /f gam.wixobj
del /q /f gam.wixpdb
c:\python27-32\scripts\pyinstaller --clean -F --distpath=gam windows-gam.spec
xcopy LICENSE gam\
xcopy whatsnew.txt gam\
del gam\w9xpopen.exe
"%ProgramFiles%\7-Zip\7z.exe" a -tzip gam-%1-windows.zip gam\ -xr!.svn
c:\python27-64\scripts\pyinstaller --clean -F --distpath=gam-64 windows-gam.spec
xcopy LICENSE gam-64\
xcopy whatsnew.txt gam-64\
"%ProgramFiles%\7-Zip\7z.exe" a -tzip gam-%1-windows-x64.zip gam-64\ -xr!.svn
set GAMVERSION=%1
"%ProgramFiles(x86)%\WiX Toolset v3.10\bin\candle.exe" -arch x64 gam.wxs
"%ProgramFiles(x86)%\WiX Toolset v3.10\bin\light.exe" -ext "%ProgramFiles(x86)%\WiX Toolset v3.10\bin\WixUIExtension.dll" gam.wixobj -o gam-%1-windows-x64.msi