gam7 7.3.7__tar.gz → 7.3.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

Files changed (109) hide show
  1. {gam7-7.3.7 → gam7-7.3.9}/.github/workflows/build.yml +7 -6
  2. {gam7-7.3.7 → gam7-7.3.9}/.github/workflows/pypi.yml +1 -0
  3. {gam7-7.3.7 → gam7-7.3.9}/PKG-INFO +11 -2
  4. {gam7-7.3.7 → gam7-7.3.9}/pyproject.toml +26 -5
  5. {gam7-7.3.7 → gam7-7.3.9}/src/GamCommands.txt +2 -0
  6. {gam7-7.3.7 → gam7-7.3.9}/src/GamUpdate.txt +29 -0
  7. {gam7-7.3.7 → gam7-7.3.9}/src/gam/__init__.py +86 -59
  8. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glglobals.py +5 -2
  9. {gam7-7.3.7 → gam7-7.3.9}/.github/ISSUE_TEMPLATE/aa-question.md +0 -0
  10. {gam7-7.3.7 → gam7-7.3.9}/.github/ISSUE_TEMPLATE/za-bug-report.md +0 -0
  11. {gam7-7.3.7 → gam7-7.3.9}/.github/ISSUE_TEMPLATE/zz-feature-request.md +0 -0
  12. {gam7-7.3.7 → gam7-7.3.9}/.github/ISSUE_TEMPLATE.txt +0 -0
  13. {gam7-7.3.7 → gam7-7.3.9}/.github/actions/creds.tar.xz.gpg +0 -0
  14. {gam7-7.3.7 → gam7-7.3.9}/.github/actions/decrypt.sh +0 -0
  15. {gam7-7.3.7 → gam7-7.3.9}/.github/actions/entitlements.plist +0 -0
  16. {gam7-7.3.7 → gam7-7.3.9}/.github/actions/package_exclusions.txt +0 -0
  17. {gam7-7.3.7 → gam7-7.3.9}/.github/stale.yml +0 -0
  18. {gam7-7.3.7 → gam7-7.3.9}/.github/workflows/codeql-analysis.yml +0 -0
  19. {gam7-7.3.7 → gam7-7.3.9}/.github/workflows/get-cacerts.yml +0 -0
  20. {gam7-7.3.7 → gam7-7.3.9}/.pre-commit-config.yaml +0 -0
  21. {gam7-7.3.7 → gam7-7.3.9}/LICENSE +0 -0
  22. {gam7-7.3.7 → gam7-7.3.9}/README.md +0 -0
  23. {gam7-7.3.7 → gam7-7.3.9}/src/.gitignore +0 -0
  24. {gam7-7.3.7 → gam7-7.3.9}/src/LICENSE +0 -0
  25. {gam7-7.3.7 → gam7-7.3.9}/src/cacerts.pem +0 -0
  26. {gam7-7.3.7 → gam7-7.3.9}/src/callgam.py +0 -0
  27. {gam7-7.3.7 → gam7-7.3.9}/src/gam/__main__.py +0 -0
  28. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/__init__.py +0 -0
  29. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/auth.py +0 -0
  30. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/client.py +0 -0
  31. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/core.py +0 -0
  32. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/data.py +0 -0
  33. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/http.py +0 -0
  34. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/http_core.py +0 -0
  35. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/http_interface.py +0 -0
  36. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/mock_http.py +0 -0
  37. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/mock_http_core.py +0 -0
  38. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/mock_service.py +0 -0
  39. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/service.py +0 -0
  40. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/token_store.py +0 -0
  41. {gam7-7.3.7 → gam7-7.3.9}/src/gam/atom/url.py +0 -0
  42. {gam7-7.3.7 → gam7-7.3.9}/src/gam/cacerts.pem +0 -0
  43. {gam7-7.3.7 → gam7-7.3.9}/src/gam/cbcm-v1.1beta1.json +0 -0
  44. {gam7-7.3.7 → gam7-7.3.9}/src/gam/contactdelegation-v1.json +0 -0
  45. {gam7-7.3.7 → gam7-7.3.9}/src/gam/datastudio-v1.json +0 -0
  46. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/__init__.py +0 -0
  47. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glaction.py +0 -0
  48. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glapi.py +0 -0
  49. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glcfg.py +0 -0
  50. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glclargs.py +0 -0
  51. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glentity.py +0 -0
  52. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glgapi.py +0 -0
  53. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glgdata.py +0 -0
  54. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glindent.py +0 -0
  55. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glmsgs.py +0 -0
  56. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glskus.py +0 -0
  57. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/gluprop.py +0 -0
  58. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/glverlibs.py +0 -0
  59. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gamlib/yubikey.py +0 -0
  60. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/__init__.py +0 -0
  61. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/alt/__init__.py +0 -0
  62. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/alt/app_engine.py +0 -0
  63. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/alt/appengine.py +0 -0
  64. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/__init__.py +0 -0
  65. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/audit/__init__.py +0 -0
  66. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/audit/service.py +0 -0
  67. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/contacts/__init__.py +0 -0
  68. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/contacts/service.py +0 -0
  69. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/service.py +0 -0
  70. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/sites/__init__.py +0 -0
  71. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/apps/sites/service.py +0 -0
  72. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/service.py +0 -0
  73. {gam7-7.3.7 → gam7-7.3.9}/src/gam/gdata/urlfetch.py +0 -0
  74. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/__init__.py +0 -0
  75. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/_auth.py +0 -0
  76. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/_helpers.py +0 -0
  77. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/channel.py +0 -0
  78. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/discovery.py +0 -0
  79. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/discovery_cache/__init__.py +0 -0
  80. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/discovery_cache/appengine_memcache.py +0 -0
  81. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/discovery_cache/base.py +0 -0
  82. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/discovery_cache/file_cache.py +0 -0
  83. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/errors.py +0 -0
  84. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/http.py +0 -0
  85. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/mimeparse.py +0 -0
  86. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/model.py +0 -0
  87. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/schema.py +0 -0
  88. {gam7-7.3.7 → gam7-7.3.9}/src/gam/googleapiclient/version.py +0 -0
  89. {gam7-7.3.7 → gam7-7.3.9}/src/gam/iso8601/__init__.py +0 -0
  90. {gam7-7.3.7 → gam7-7.3.9}/src/gam/iso8601/iso8601.py +0 -0
  91. {gam7-7.3.7 → gam7-7.3.9}/src/gam/serviceaccountlookup-v1.json +0 -0
  92. {gam7-7.3.7 → gam7-7.3.9}/src/gam/six.py +0 -0
  93. {gam7-7.3.7 → gam7-7.3.9}/src/gam-install.sh +0 -0
  94. {gam7-7.3.7 → gam7-7.3.9}/src/gam-setup.bat +0 -0
  95. {gam7-7.3.7 → gam7-7.3.9}/src/gam.exe.manifest +0 -0
  96. {gam7-7.3.7 → gam7-7.3.9}/src/gam.py +0 -0
  97. {gam7-7.3.7 → gam7-7.3.9}/src/gam.spec +0 -0
  98. {gam7-7.3.7 → gam7-7.3.9}/src/gam.wxs +0 -0
  99. {gam7-7.3.7 → gam7-7.3.9}/src/license.rtf +0 -0
  100. {gam7-7.3.7 → gam7-7.3.9}/src/project-apis.txt +0 -0
  101. {gam7-7.3.7 → gam7-7.3.9}/src/requirements-dev.txt +0 -0
  102. {gam7-7.3.7 → gam7-7.3.9}/src/requirements.txt +0 -0
  103. {gam7-7.3.7 → gam7-7.3.9}/src/setup.cfg +0 -0
  104. {gam7-7.3.7 → gam7-7.3.9}/src/setup.py +0 -0
  105. {gam7-7.3.7 → gam7-7.3.9}/src/tools/a_atleast_b.py +0 -0
  106. {gam7-7.3.7 → gam7-7.3.9}/src/tools/gen-wix-xml-filelist.py +0 -0
  107. {gam7-7.3.7 → gam7-7.3.9}/src/tools/mkGamRef.py +0 -0
  108. {gam7-7.3.7 → gam7-7.3.9}/src/tools/openssl.props +0 -0
  109. {gam7-7.3.7 → gam7-7.3.9}/src/version_info.txt.in +0 -0
@@ -5,6 +5,7 @@ on:
5
5
  pull_request:
6
6
  schedule:
7
7
  - cron: '37 22 * * *'
8
+ workflow_dispatch:
8
9
 
9
10
  permissions:
10
11
  contents: read
@@ -756,7 +757,7 @@ jobs:
756
757
 
757
758
  - name: Attest that gam package files were generated from this Action
758
759
  uses: actions/attest-build-provenance@v1
759
- if: (github.event_name == 'push' || github.event_name == 'schedule') && matrix.goal == 'build'
760
+ if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal == 'build'
760
761
  with:
761
762
  subject-path: |
762
763
  gam*.tar.xz
@@ -765,7 +766,7 @@ jobs:
765
766
 
766
767
  - name: Archive production artifacts
767
768
  uses: actions/upload-artifact@v4
768
- if: (github.event_name == 'push' || github.event_name == 'schedule') && matrix.goal != 'test'
769
+ if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal != 'test'
769
770
  with:
770
771
  name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
771
772
  path: |
@@ -793,8 +794,8 @@ jobs:
793
794
  fi
794
795
  echo "We successfully compiled Python ${this_python} and OpenSSL ${this_openssl}"
795
796
 
796
- - name: Live API tests push only
797
- if: (github.event_name == 'push' || github.event_name == 'schedule')
797
+ - name: Live API tests
798
+ if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
798
799
  run: |
799
800
  export gam_user="gam-gha-${JID}@pdl.jaylee.us"
800
801
  echo "gam_user=${gam_user}" >> $GITHUB_ENV
@@ -1004,7 +1005,7 @@ jobs:
1004
1005
  tar cJvvf cache.tar.xz $tar_folders
1005
1006
 
1006
1007
  merge:
1007
- if: (github.event_name == 'push' || github.event_name == 'schedule')
1008
+ if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
1008
1009
  runs-on: ubuntu-24.04
1009
1010
  needs: build
1010
1011
  permissions:
@@ -1018,7 +1019,7 @@ jobs:
1018
1019
  pattern: gam-binaries-*
1019
1020
 
1020
1021
  publish:
1021
- if: github.event_name == 'push'
1022
+ if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
1022
1023
  runs-on: ubuntu-24.04
1023
1024
  needs: merge
1024
1025
  permissions:
@@ -3,6 +3,7 @@ on:
3
3
  push:
4
4
  tags:
5
5
  - 'v[0-9]+.[0-9]+.[0-9]+'
6
+ workflow_dispatch:
6
7
 
7
8
  jobs:
8
9
  pypi:
@@ -1,14 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gam7
3
- Version: 7.3.7
3
+ Version: 7.3.9
4
4
  Summary: CLI tool to manage Google Workspace
5
5
  Project-URL: Homepage, https://github.com/GAM-team/GAM
6
6
  Project-URL: Issues, https://github.com/GAM-team/GAM/issues
7
+ Project-URL: Discussion, https://groups.google.com/group/google-apps-manager
8
+ Project-URL: Chat, https://git.io/gam-chat
7
9
  Author-email: Jay Lee <jay0lee@gmail.com>, Ross Scroggs <Ross.Scroggs@gmail.com>
8
10
  License: Apache License (2.0)
9
11
  License-File: LICENSE
10
12
  Classifier: Operating System :: OS Independent
11
13
  Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
12
20
  Requires-Python: >=3.9
13
21
  Requires-Dist: chardet
14
22
  Requires-Dist: cryptography
@@ -23,7 +31,8 @@ Requires-Dist: lxml
23
31
  Requires-Dist: passlib>=1.7.2
24
32
  Requires-Dist: pathvalidate
25
33
  Requires-Dist: python-dateutil
26
- Requires-Dist: yubikey-manager[yubikey]>=5.0
34
+ Provides-Extra: yubikey
35
+ Requires-Dist: yubikey-manager>=5.0; extra == 'yubikey'
27
36
  Description-Content-Type: text/markdown
28
37
 
29
38
  GAM is a command line tool for Google Workspace admins to manage domain and user settings quickly and easily.
@@ -1,29 +1,54 @@
1
1
  [project]
2
2
  name = "gam7"
3
3
  dynamic = [
4
- "dependencies",
5
4
  "version",
6
5
  ]
7
6
  authors = [
8
7
  { name="Jay Lee", email="jay0lee@gmail.com" },
9
8
  { name="Ross Scroggs", email="Ross.Scroggs@gmail.com" },
10
9
  ]
10
+ dependencies = [
11
+ "chardet",
12
+ "cryptography",
13
+ "distro; sys_platform=='linux'",
14
+ "filelock",
15
+ "google-api-python-client>=2.1",
16
+ "google-auth-httplib2",
17
+ "google-auth-oauthlib>=0.4.1",
18
+ "google-auth>=2.3.2",
19
+ "httplib2>=0.17.0",
20
+ "lxml",
21
+ "passlib>=1.7.2",
22
+ "pathvalidate",
23
+ "python-dateutil",
24
+ ]
11
25
  description = "CLI tool to manage Google Workspace"
12
26
  readme = "README.md"
13
27
  requires-python = ">=3.9"
14
28
  classifiers = [
15
29
  "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3 :: Only",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
16
36
  "Operating System :: OS Independent",
17
37
  ]
18
38
  license = {text = "Apache License (2.0)"}
19
39
  license-files = ["LICEN[CS]E*"]
20
40
 
41
+ [project.optional-dependencies]
42
+ yubikey = ["yubikey-manager>=5.0"]
43
+
21
44
  [project.scripts]
22
45
  gam = "gam.__main__:main"
23
46
 
24
47
  [project.urls]
25
48
  Homepage = "https://github.com/GAM-team/GAM"
26
49
  Issues = "https://github.com/GAM-team/GAM/issues"
50
+ Discussion = "https://groups.google.com/group/google-apps-manager"
51
+ Chat = "https://git.io/gam-chat"
27
52
 
28
53
  [tool.hatch.version]
29
54
  path = "src/gam/__init__.py"
@@ -31,12 +56,8 @@ path = "src/gam/__init__.py"
31
56
  [tool.hatch.build.targets.wheel]
32
57
  packages = ["src/gam"]
33
58
 
34
- [tool.hatch.metadata.hooks.requirements_txt]
35
- files = ["src/requirements.txt"]
36
-
37
59
  [build-system]
38
60
  requires = [
39
61
  "hatchling",
40
- "hatch-requirements_txt",
41
62
  ]
42
63
  build-backend = "hatchling.build"
@@ -7606,6 +7606,7 @@ gam <UserTypeEntity> show messages|threads
7606
7606
  [countsonly|positivecountsonly] [useronly]
7607
7607
  [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
7608
7608
  [showlabels] [delimiter <Character>] [showbody] [showhtml] [showdate] [showsize] [showsnippet]
7609
+ [maxmessagesperthread <Number>]
7609
7610
  [[attachmentnamepattern <RegularExpression>]
7610
7611
  [showattachments [noshowtextplain]]
7611
7612
  [saveattachments [targetfolder <FilePath>] [overwrite [<Boolean>]]]
@@ -7617,6 +7618,7 @@ gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*]
7617
7618
  [countsonly|positivecountsonly] [useronly]
7618
7619
  [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String> [dateheaderconverttimezone [<Boolean>]]]
7619
7620
  [showlabels] [delimiter <Character>] [showbody] [showhtml] [showdate] [showsize] [showsnippet]
7621
+ [maxmessagesperthread <Number>]
7620
7622
  [convertcrnl] [delimiter <Character>]
7621
7623
  [[attachmentnamepattern <RegularExpression>]
7622
7624
  [showattachments [noshowtextplain]]]
@@ -1,3 +1,32 @@
1
+ 7.03.09
2
+
3
+ Added option `maxmessagesperthread <Number>` to `gam <UserTypeEntity> print|show threads`
4
+ that limits the number of messages displayed per thread. The default is 0, there is no limit.
5
+ For example, this can be used if you anly want to see the first message of each thread.
6
+ ```
7
+ gam user user@domain.com print|show threads maxmessagesperthread 1
8
+ ```
9
+
10
+ Fixed bug in `gam <UserTypeEntity> print filelist countsonly` where extraneous columns
11
+ were displayed.
12
+
13
+ Fixed bug in `gam <UserTypeEntity> print filelist countsonly showsize` where sizes were
14
+ all shown as 0 unless`sizefield size` was specified.
15
+
16
+ 7.03.08
17
+
18
+ Improved pip install.
19
+
20
+ Yubikey as optional should now be working also. So:
21
+
22
+ pip install --upgrade gam7
23
+
24
+ skips Yubikey.
25
+
26
+ To install with yubikey support (assuming you have installed the necessary swig and libpcsclite-dev packages already) run:
27
+
28
+ pip install --upgrade gam7[yubikey]
29
+
1
30
  7.03.07
2
31
 
3
32
  Updated `gam create vaultexport` to include `corpus gemini`.
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
25
25
  """
26
26
 
27
27
  __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
28
- __version__ = '7.03.07'
28
+ __version__ = '7.03.09'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
@@ -8495,14 +8495,18 @@ class CSVPrintFile():
8495
8495
  self.AddTitle(title)
8496
8496
  return RowFilterMatch(row, self.titlesList, self.rowFilter, self.rowFilterMode, self.rowDropFilter, self.rowDropFilterMode)
8497
8497
 
8498
- def UpdateMimeTypeCounts(self, row, mimeTypeInfo):
8498
+ def UpdateMimeTypeCounts(self, row, mimeTypeInfo, sizeField):
8499
+ saveList = self.titlesList[:]
8500
+ saveSet = set(self.titlesSet)
8499
8501
  for title in row:
8500
8502
  if title not in self.titlesSet:
8501
8503
  self.AddTitle(title)
8502
8504
  if RowFilterMatch(row, self.titlesList, self.rowFilter, self.rowFilterMode, self.rowDropFilter, self.rowDropFilterMode):
8503
8505
  mimeTypeInfo.setdefault(row['mimeType'], {'count': 0, 'size': 0})
8504
8506
  mimeTypeInfo[row['mimeType']]['count'] += 1
8505
- mimeTypeInfo[row['mimeType']]['size'] += int(row.get('size', '0'))
8507
+ mimeTypeInfo[row['mimeType']]['size'] += int(row.get(sizeField, '0'))
8508
+ self.titlesList = saveList[:]
8509
+ self.titlesSet = set(saveSet)
8506
8510
 
8507
8511
  def SetZeroBlankMimeTypeCounts(self, zeroBlankMimeTypeCounts):
8508
8512
  self.zeroBlankMimeTypeCounts = zeroBlankMimeTypeCounts
@@ -41661,23 +41665,22 @@ def printShowUserVaultHolds(entityList):
41661
41665
  else:
41662
41666
  printKeyValueList(['Total Holds', totalHolds])
41663
41667
 
41664
- def _cleanVaultQuery(query, cd, drive):
41668
+ def _cleanVaultQuery(query, cd):
41665
41669
  if 'query' in query:
41666
41670
  if cd is not None:
41667
41671
  if 'orgUnitInfo' in query['query']:
41668
41672
  query['query']['orgUnitInfo']['orgUnitPath'] = convertOrgUnitIDtoPath(cd, query['query']['orgUnitInfo']['orgUnitId'])
41669
- if drive is not None:
41670
41673
  if 'sharedDriveInfo' in query['query']:
41671
41674
  query['query']['sharedDriveInfo']['sharedDriveNames'] = []
41672
41675
  for sharedDriveId in query['query']['sharedDriveInfo']['sharedDriveIds']:
41673
- query['query']['sharedDriveInfo']['sharedDriveNames'].append(_getSharedDriveNameFromId(drive, sharedDriveId, useDomainAdminAccess=True))
41676
+ query['query']['sharedDriveInfo']['sharedDriveNames'].append(_getSharedDriveNameFromId(sharedDriveId))
41674
41677
  query['query'].pop('searchMethod', None)
41675
41678
  query['query'].pop('teamDriveInfo', None)
41676
41679
 
41677
41680
  VAULT_QUERY_TIME_OBJECTS = {'createTime', 'endTime', 'startTime', 'versionDate'}
41678
41681
 
41679
- def _showVaultQuery(matterNameId, query, cd, drive, FJQC, k=0, kcount=0):
41680
- _cleanVaultQuery(query, cd, drive)
41682
+ def _showVaultQuery(matterNameId, query, cd, FJQC, k=0, kcount=0):
41683
+ _cleanVaultQuery(query, cd)
41681
41684
  if FJQC is not None and FJQC.formatJSON:
41682
41685
  printLine(json.dumps(cleanJSON(query, timeObjects=VAULT_QUERY_TIME_OBJECTS), ensure_ascii=False, sort_keys=False))
41683
41686
  return
@@ -41709,7 +41712,7 @@ def doInfoVaultQuery():
41709
41712
  queryId, queryName, queryNameId = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
41710
41713
  else:
41711
41714
  queryName = getString(Cmd.OB_QUERY_ITEM)
41712
- cd = drive = None
41715
+ cd = None
41713
41716
  fieldsList = []
41714
41717
  FJQC = FormatJSONQuoteChar()
41715
41718
  while Cmd.ArgumentsRemaining():
@@ -41719,8 +41722,8 @@ def doInfoVaultQuery():
41719
41722
  queryId, queryName, queryNameId = convertQueryNameToID(v, queryName, matterId, matterNameId)
41720
41723
  elif myarg == 'shownames':
41721
41724
  cd = buildGAPIObject(API.DIRECTORY)
41722
- _, drive = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
41723
- if drive is None:
41725
+ _, GM.Globals[GM.ADMIN_DRIVE] = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
41726
+ if GM.Globals[GM.ADMIN_DRIVE] is None:
41724
41727
  return
41725
41728
  elif getFieldsList(myarg, VAULT_QUERY_FIELDS_CHOICE_MAP, fieldsList, initialField=['savedQueryId', 'displayName']):
41726
41729
  pass
@@ -41731,7 +41734,7 @@ def doInfoVaultQuery():
41731
41734
  query = callGAPI(v.matters().savedQueries(), 'get',
41732
41735
  throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN],
41733
41736
  matterId=matterId, savedQueryId=queryId, fields=fields)
41734
- _showVaultQuery(matterNameId, query, cd, drive, FJQC)
41737
+ _showVaultQuery(matterNameId, query, cd, FJQC)
41735
41738
  except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden) as e:
41736
41739
  entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e))
41737
41740
 
@@ -41748,7 +41751,7 @@ def doPrintShowVaultQueries():
41748
41751
  csvPF = CSVPrintFile(PRINT_VAULT_QUERIES_TITLES, 'sortall') if Act.csvFormat() else None
41749
41752
  FJQC = FormatJSONQuoteChar(csvPF)
41750
41753
  matters = []
41751
- cd = drive = None
41754
+ cd = None
41752
41755
  fieldsList = []
41753
41756
  while Cmd.ArgumentsRemaining():
41754
41757
  myarg = getArgument()
@@ -41758,8 +41761,8 @@ def doPrintShowVaultQueries():
41758
41761
  matters = shlexSplitList(getString(Cmd.OB_MATTER_ITEM_LIST))
41759
41762
  elif myarg == 'shownames':
41760
41763
  cd = buildGAPIObject(API.DIRECTORY)
41761
- _, drive = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
41762
- if drive is None:
41764
+ _, GM.Globals[GM.ADMIN_DRIVE] = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
41765
+ if GM.Globals[GM.ADMIN_DRIVE] is None:
41763
41766
  return
41764
41767
  elif getFieldsList(myarg, VAULT_QUERY_FIELDS_CHOICE_MAP, fieldsList, initialField=['savedQueryId', 'displayName']):
41765
41768
  pass
@@ -41821,11 +41824,11 @@ def doPrintShowVaultQueries():
41821
41824
  k = 0
41822
41825
  for query in queries:
41823
41826
  k += 1
41824
- _showVaultQuery(matterNameId, query, cd, drive, FJQC, k, kcount)
41827
+ _showVaultQuery(matterNameId, query, cd, FJQC, k, kcount)
41825
41828
  Ind.Decrement()
41826
41829
  else:
41827
41830
  for query in queries:
41828
- _cleanVaultQuery(query, cd, drive)
41831
+ _cleanVaultQuery(query, cd)
41829
41832
  row = flattenJSON(query, flattened={'matterId': matterId, 'matterName': matterName}, timeObjects=VAULT_QUERY_TIME_OBJECTS)
41830
41833
  if not FJQC.formatJSON:
41831
41834
  csvPF.WriteRowTitles(row)
@@ -52817,15 +52820,20 @@ def _convertSharedDriveNameToId(drive, user, i, count, fileIdEntity, useDomainAd
52817
52820
  ','.join([td['id'] for td in tdlist])), i, count)
52818
52821
  return False
52819
52822
 
52820
- def _getSharedDriveNameFromId(drive, sharedDriveId, useDomainAdminAccess=False):
52823
+ def _getSharedDriveNameFromId(sharedDriveId):
52821
52824
  sharedDriveName = GM.Globals[GM.MAP_SHAREDDRIVE_ID_TO_NAME].get(sharedDriveId)
52822
52825
  if not sharedDriveName:
52823
- try:
52824
- sharedDriveName = callGAPI(drive.drives(), 'get',
52825
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
52826
- useDomainAdminAccess=useDomainAdminAccess,
52827
- driveId=sharedDriveId, fields='name')['name']
52828
- except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
52826
+ if not GM.Globals[GM.ADMIN_DRIVE]:
52827
+ _, GM.Globals[GM.ADMIN_DRIVE] = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
52828
+ if GM.Globals[GM.ADMIN_DRIVE]:
52829
+ try:
52830
+ sharedDriveName = callGAPI(GM.Globals[GM.ADMIN_DRIVE].drives(), 'get',
52831
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
52832
+ useDomainAdminAccess=True,
52833
+ driveId=sharedDriveId, fields='name')['name']
52834
+ except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
52835
+ sharedDriveName = TEAM_DRIVE
52836
+ else:
52829
52837
  sharedDriveName = TEAM_DRIVE
52830
52838
  GM.Globals[GM.MAP_SHAREDDRIVE_ID_TO_NAME][sharedDriveId] = sharedDriveName
52831
52839
  return sharedDriveName
@@ -52838,7 +52846,7 @@ def _getDriveFileNameFromId(drive, fileId, combineTitleId=True, useDomainAdminAc
52838
52846
  if result:
52839
52847
  fileName = result['name']
52840
52848
  if (result['mimeType'] == MIMETYPE_GA_FOLDER) and result.get('driveId') and (result['name'] == TEAM_DRIVE):
52841
- fileName = _getSharedDriveNameFromId(drive, result['driveId'])
52849
+ fileName = _getSharedDriveNameFromId(result['driveId'])
52842
52850
  if combineTitleId:
52843
52851
  fileName += '('+fileId+')'
52844
52852
  return (fileName, _getEntityMimeType(result), result['mimeType'])
@@ -53920,7 +53928,7 @@ def getFilePaths(drive, fileTree, initialResult, filePathInfo, addParentsToTree=
53920
53928
  fullpath=False, showDepth=False, folderPathOnly=False):
53921
53929
  def _getParentName(result):
53922
53930
  if (result['mimeType'] == MIMETYPE_GA_FOLDER) and result.get('driveId') and (result['name'] == TEAM_DRIVE):
53923
- parentName = _getSharedDriveNameFromId(drive, result['driveId'])
53931
+ parentName = _getSharedDriveNameFromId(result['driveId'])
53924
53932
  if parentName != TEAM_DRIVE:
53925
53933
  return f'{SHARED_DRIVES}{filePathInfo["delimiter"]}{parentName}'
53926
53934
  return result['name']
@@ -54619,9 +54627,9 @@ def showFileInfo(users):
54619
54627
  driveId = result.get('driveId')
54620
54628
  if driveId:
54621
54629
  if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE:
54622
- result['name'] = _getSharedDriveNameFromId(drive, driveId)
54630
+ result['name'] = _getSharedDriveNameFromId(driveId)
54623
54631
  if DFF.showSharedDriveNames:
54624
- result['driveName'] = _getSharedDriveNameFromId(drive, driveId)
54632
+ result['driveName'] = _getSharedDriveNameFromId(driveId)
54625
54633
  if showNoParents:
54626
54634
  result.setdefault('parents', [])
54627
54635
  if getPermissionsForSharedDrives and driveId and 'permissions' not in result:
@@ -55309,7 +55317,7 @@ def extendFileTreeParents(drive, fileTree, fields):
55309
55317
  result['parents'] = [ORPHANS] if result.get('ownedByMe', False) and 'sharedWithMeTime' not in result else [SHARED_WITHME]
55310
55318
  else:
55311
55319
  if result['name'] == TEAM_DRIVE:
55312
- result['name'] = _getSharedDriveNameFromId(drive, result['driveId'])
55320
+ result['name'] = _getSharedDriveNameFromId(result['driveId'])
55313
55321
  result['parents'] = [SHARED_DRIVES] if 'sharedWithMeTime' not in result else [SHARED_WITHME]
55314
55322
  fileTree[fileId]['info'] = result
55315
55323
  fileTree[fileId]['info']['noDisplay'] = True
@@ -56048,7 +56056,7 @@ def printFileList(users):
56048
56056
  if not pmselect and 'permissions' in fileInfo:
56049
56057
  fileInfo['permissions'] = DLP.GetFileMatchingPermission(fileInfo)
56050
56058
  if DFF.showSharedDriveNames and driveId:
56051
- fileInfo['driveName'] = _getSharedDriveNameFromId(drive, driveId)
56059
+ fileInfo['driveName'] = _getSharedDriveNameFromId(driveId)
56052
56060
  if filepath:
56053
56061
  if not FJQC.formatJSON or not addPathsToJSON:
56054
56062
  addFilePathsToRow(drive, fileTree, fileInfo, filePathInfo, csvPF, row,
@@ -56103,7 +56111,7 @@ def printFileList(users):
56103
56111
  else:
56104
56112
  if not countsRowFilter:
56105
56113
  csvPFco.UpdateMimeTypeCounts(flattenJSON(fileInfo, flattened=row, skipObjects=skipObjects, timeObjects=timeObjects,
56106
- simpleLists=simpleLists, delimiter=delimiter), mimeTypeInfo)
56114
+ simpleLists=simpleLists, delimiter=delimiter), mimeTypeInfo, sizeField)
56107
56115
  else:
56108
56116
  mimeTypeInfo.setdefault(fileInfo['mimeType'], {'count': 0, 'size': 0})
56109
56117
  mimeTypeInfo[fileInfo['mimeType']]['count'] += 1
@@ -56924,7 +56932,7 @@ def printShowFilePaths(users):
56924
56932
  driveId = result.get('driveId')
56925
56933
  if driveId:
56926
56934
  if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE:
56927
- result['name'] = _getSharedDriveNameFromId(drive, driveId)
56935
+ result['name'] = _getSharedDriveNameFromId(driveId)
56928
56936
  if returnPathOnly:
56929
56937
  if fullpath:
56930
56938
  writeStdout(f'{SHARED_DRIVES}/{result["name"]}\n')
@@ -57014,7 +57022,7 @@ def printFileParentTree(users):
57014
57022
  driveId = result.get('driveId')
57015
57023
  if driveId:
57016
57024
  if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE:
57017
- result['name'] = _getSharedDriveNameFromId(drive, driveId)
57025
+ result['name'] = _getSharedDriveNameFromId(driveId)
57018
57026
  result['isRoot'] = True
57019
57027
  result['parents'] = ['']
57020
57028
  fileList.append(result)
@@ -57212,10 +57220,7 @@ def printShowFileCounts(users):
57212
57220
  if not drive:
57213
57221
  continue
57214
57222
  sharedDriveId = fileIdEntity.get('shareddrive', {}).get('driveId', '')
57215
- if sharedDriveId:
57216
- sharedDriveName = _getSharedDriveNameFromId(drive, sharedDriveId)
57217
- else:
57218
- sharedDriveName = ''
57223
+ sharedDriveName = _getSharedDriveNameFromId(sharedDriveId) if sharedDriveId else ''
57219
57224
  mimeTypeInfo = {}
57220
57225
  userLastModification = {
57221
57226
  'lastModifiedFileId': '', 'lastModifiedFileName': '',
@@ -57444,7 +57449,7 @@ def printDiskUsage(users):
57444
57449
  includeOwner = False
57445
57450
  csvPF.RemoveTitles(['Owner', 'ownedByMe'])
57446
57451
  if topFolder['name'] == TEAM_DRIVE and not topFolder.get('parents'):
57447
- topFolder['name'] = _getSharedDriveNameFromId(drive, driveId)
57452
+ topFolder['name'] = _getSharedDriveNameFromId(driveId)
57448
57453
  topFolder['path'] = f'{SHARED_DRIVES}{pathDelimiter}{topFolder["name"]}'
57449
57454
  else:
57450
57455
  topFolder['path'] = topFolder['name']
@@ -59509,7 +59514,7 @@ def _getCopyMoveParentInfo(drive, user, i, count, j, jcount, newParentId, statis
59509
59514
  result['destParentType'] = DEST_PARENT_MYDRIVE_FOLDER
59510
59515
  else:
59511
59516
  if result['name'] == TEAM_DRIVE and not result.get('parents', []):
59512
- result['name'] = _getSharedDriveNameFromId(drive, result['driveId'])
59517
+ result['name'] = _getSharedDriveNameFromId(result['driveId'])
59513
59518
  result['destParentType'] = DEST_PARENT_SHAREDDRIVE_ROOT
59514
59519
  else:
59515
59520
  result['destParentType'] = DEST_PARENT_SHAREDDRIVE_FOLDER
@@ -60055,7 +60060,7 @@ def copyDriveFile(users):
60055
60060
  # Source at root of Shared Drive?
60056
60061
  sourceMimeType = source['mimeType']
60057
60062
  if sourceMimeType == MIMETYPE_GA_FOLDER and source.get('driveId') and source['name'] == TEAM_DRIVE and not source.get('parents', []):
60058
- source['name'] = _getSharedDriveNameFromId(drive, source['driveId'])
60063
+ source['name'] = _getSharedDriveNameFromId(source['driveId'])
60059
60064
  sourceName = source['name']
60060
60065
  sourceNameId = f"{sourceName}({source['id']})"
60061
60066
  copyMoveOptions['sourceDriveId'] = source.get('driveId')
@@ -60823,7 +60828,7 @@ def moveDriveFile(users):
60823
60828
  if sourceMimeType == MIMETYPE_GA_FOLDER and source['name'] in [MY_DRIVE, TEAM_DRIVE] and not source.get('parents', []):
60824
60829
  copyMoveOptions['sourceIsMyDriveSharedDrive'] = True
60825
60830
  if source.get('driveId'):
60826
- source['name'] = _getSharedDriveNameFromId(drive, source['driveId'])
60831
+ source['name'] = _getSharedDriveNameFromId(source['driveId'])
60827
60832
  sourceName = source['name']
60828
60833
  sourceNameId = f"{sourceName}({source['id']})"
60829
60834
  copyMoveOptions['sourceDriveId'] = source.get('driveId')
@@ -63248,7 +63253,7 @@ def printEmptyDriveFolders(users):
63248
63253
  fileIdEntity['shareddrive'] = {'driveId': sharedDriveId, 'corpora': 'drive', 'includeItemsFromAllDrives': True, 'supportsAllDrives': True}
63249
63254
  csvPF.AddTitles(['driveId'])
63250
63255
  csvPF.MoveTitlesToEnd(['name'])
63251
- pathList = [f'{SHARED_DRIVES}/{_getSharedDriveNameFromId(drive, sharedDriveId)}']
63256
+ pathList = [f'{SHARED_DRIVES}/{_getSharedDriveNameFromId(sharedDriveId)}']
63252
63257
  else:
63253
63258
  pathList = [fileEntryInfo['name']]
63254
63259
  mimeType = fileEntryInfo['mimeType']
@@ -63338,7 +63343,7 @@ def deleteEmptyDriveFolders(users):
63338
63343
  if 'driveId' in fileEntryInfo:
63339
63344
  sharedDriveId = fileEntryInfo['driveId']
63340
63345
  fileIdEntity['shareddrive'] = {'driveId': sharedDriveId, 'corpora': 'drive', 'includeItemsFromAllDrives': True, 'supportsAllDrives': True}
63341
- pathList = [f'{SHARED_DRIVES}/{_getSharedDriveNameFromId(drive, sharedDriveId)}']
63346
+ pathList = [f'{SHARED_DRIVES}/{_getSharedDriveNameFromId(sharedDriveId)}']
63342
63347
  else:
63343
63348
  pathList = [fileEntryInfo['name']]
63344
63349
  mimeType = fileEntryInfo['mimeType']
@@ -65754,7 +65759,7 @@ def doPrintShowSharedDrives():
65754
65759
  def doPrintShowOrgunitSharedDrives():
65755
65760
  def _getOrgUnitSharedDriveInfo(shareddrive):
65756
65761
  shareddrive['driveId'] = shareddrive['name'].rsplit(';')[1]
65757
- shareddrive['driveName'] = _getSharedDriveNameFromId(drive, shareddrive['driveId'], useDomainAdminAccess=True)
65762
+ shareddrive['driveName'] = _getSharedDriveNameFromId(shareddrive['driveId'])
65758
65763
  shareddrive['orgUnitPath'] = orgUnitPath
65759
65764
 
65760
65765
  def _showOrgUnitSharedDrive(shareddrive, j, jcount, FJQC):
@@ -65773,8 +65778,8 @@ def doPrintShowOrgunitSharedDrives():
65773
65778
 
65774
65779
  ci = buildGAPIObject(API.CLOUDIDENTITY_ORGUNITS_BETA)
65775
65780
  cd = buildGAPIObject(API.DIRECTORY)
65776
- _, drive = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
65777
- if not drive:
65781
+ _, GM.Globals[GM.ADMIN_DRIVE] = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
65782
+ if not GM.Globals[GM.ADMIN_DRIVE]:
65778
65783
  return
65779
65784
  csvPF = CSVPrintFile(['name', 'type', 'member', 'memberUri', 'driveId', 'driveName', 'orgUnitPath']) if Act.csvFormat() else None
65780
65785
  FJQC = FormatJSONQuoteChar(csvPF)
@@ -65853,13 +65858,13 @@ def copySyncSharedDriveACLs(users, useDomainAdminAccess=False):
65853
65858
  if not drive:
65854
65859
  continue
65855
65860
  if not srcFileIdEntity.get('shareddrivename'):
65856
- srcFileIdEntity['shareddrivename'] = _getSharedDriveNameFromId(drive, srcFileIdEntity['shareddrive']['driveId'])
65861
+ srcFileIdEntity['shareddrivename'] = _getSharedDriveNameFromId(srcFileIdEntity['shareddrive']['driveId'])
65857
65862
  if tgtFileIdEntity.get('shareddrivename'):
65858
65863
  if not _convertSharedDriveNameToId(drive, user, i, count, tgtFileIdEntity, useDomainAdminAccess):
65859
65864
  continue
65860
65865
  tgtFileIdEntity['shareddrive']['corpora'] = 'drive'
65861
65866
  else:
65862
- tgtFileIdEntity['shareddrivename'] = _getSharedDriveNameFromId(drive, tgtFileIdEntity['shareddrive']['driveId'])
65867
+ tgtFileIdEntity['shareddrivename'] = _getSharedDriveNameFromId(tgtFileIdEntity['shareddrive']['driveId'])
65863
65868
  statistics = _initStatistics()
65864
65869
  copyMoveOptions['sourceDriveId'] = srcFileIdEntity['shareddrive']['driveId']
65865
65870
  copyMoveOptions['destDriveId'] = tgtFileIdEntity['shareddrive']['driveId']
@@ -69692,7 +69697,7 @@ def _initMessageThreadParameters(entityType, doIt, maxToProcess):
69692
69697
  'query': '', 'queryTimes': {},
69693
69698
  'entityType': entityType, 'messageEntity': None, 'doIt': doIt, 'quick': True,
69694
69699
  'labelMatchPattern': None, 'senderMatchPattern': None,
69695
- 'maxToProcess': maxToProcess, 'maxItems': 0,
69700
+ 'maxToProcess': maxToProcess, 'maxItems': 0, 'maxMessagesPerThread': 0,
69696
69701
  'maxToKeywords': [MESSAGES_MAX_TO_KEYWORDS[Act.Get()], 'maxtoprocess'],
69697
69702
  'listType': listType, 'fields': f'nextPageToken,{listType}(id)'}
69698
69703
 
@@ -69734,6 +69739,8 @@ def _getMessageSelectParameters(myarg, parameters):
69734
69739
  parameters['doIt'] = True
69735
69740
  elif myarg in parameters['maxToKeywords']:
69736
69741
  parameters['maxToProcess'] = getInteger(minVal=0)
69742
+ elif myarg == 'maxmessagesperthread':
69743
+ parameters['maxMessagesPerThread'] = getInteger(minVal=0)
69737
69744
  else:
69738
69745
  return False
69739
69746
  return True
@@ -70873,8 +70880,6 @@ def printShowMessagesThreads(users, entityType):
70873
70880
  return None
70874
70881
 
70875
70882
  def _qualifyMessage(user, result):
70876
- if parameters['maxToProcess'] and parameters['messagesProcessed'] == parameters['maxToProcess']:
70877
- return (False, None)
70878
70883
  if senderMatchPattern:
70879
70884
  sender = _checkSenderMatchCount(result)
70880
70885
  if not sender:
@@ -70902,7 +70907,9 @@ def printShowMessagesThreads(users, entityType):
70902
70907
  except ValueError:
70903
70908
  return headerValue
70904
70909
 
70905
- def _showMessage(user, result, j, jcount):
70910
+ def _showMessage(user, result, j, jcount, checkMax=True):
70911
+ if checkMax and parameters['maxToProcess'] and parameters['messagesProcessed'] == parameters['maxToProcess']:
70912
+ return
70906
70913
  status, messageLabels = _qualifyMessage(user, result)
70907
70914
  if not status:
70908
70915
  return
@@ -70938,7 +70945,8 @@ def printShowMessagesThreads(users, entityType):
70938
70945
  if show_attachments or save_attachments or upload_attachments:
70939
70946
  _showSaveAttachments(result['id'], result['payload'], attachmentNamePattern, j, jcount)
70940
70947
  Ind.Decrement()
70941
- parameters['messagesProcessed'] += 1
70948
+ if checkMax:
70949
+ parameters['messagesProcessed'] += 1
70942
70950
 
70943
70951
  def _getAttachments(messageId, payload, attachmentNamePattern, attachments):
70944
70952
  for part in payload.get('parts', []):
@@ -70960,7 +70968,9 @@ def printShowMessagesThreads(users, entityType):
70960
70968
  else:
70961
70969
  _getAttachments(messageId, part, attachmentNamePattern, attachments)
70962
70970
 
70963
- def _printMessage(user, result):
70971
+ def _printMessage(user, result, checkMax=True):
70972
+ if checkMax and parameters['maxToProcess'] and parameters['messagesProcessed'] == parameters['maxToProcess']:
70973
+ return
70964
70974
  status, messageLabels = _qualifyMessage(user, result)
70965
70975
  if not status:
70966
70976
  return
@@ -71014,7 +71024,8 @@ def printShowMessagesThreads(users, entityType):
71014
71024
  row[f'Attachments{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{i}{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}size'] = attachment[2]
71015
71025
  row[f'Attachments{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{i}{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}charset'] = attachment[3]
71016
71026
  csvPF.WriteRowTitles(row)
71017
- parameters['messagesProcessed'] += 1
71027
+ if checkMax:
71028
+ parameters['messagesProcessed'] += 1
71018
71029
 
71019
71030
  def _countMessageLabels(user, result):
71020
71031
  if senderMatchPattern:
@@ -71044,6 +71055,8 @@ def printShowMessagesThreads(users, entityType):
71044
71055
  messageThreadCounts['size'] += result['sizeEstimate']
71045
71056
 
71046
71057
  def _showThread(user, result, j, jcount):
71058
+ if parameters['maxToProcess'] and parameters['messagesProcessed'] == parameters['maxToProcess']:
71059
+ return
71047
71060
  if senderMatchPattern:
71048
71061
  for message in result['messages']:
71049
71062
  if _checkSenderMatch(message):
@@ -71059,19 +71072,29 @@ def printShowMessagesThreads(users, entityType):
71059
71072
  k = 0
71060
71073
  for message in result['messages']:
71061
71074
  k += 1
71062
- _showMessage(user, message, k, kcount)
71075
+ _showMessage(user, message, k, kcount, False)
71076
+ if k == parameters['maxMessagesPerThread']:
71077
+ break
71063
71078
  Ind.Decrement()
71079
+ parameters['messagesProcessed'] += 1
71064
71080
 
71065
71081
  def _printThread(user, result):
71082
+ if parameters['maxToProcess'] and parameters['messagesProcessed'] == parameters['maxToProcess']:
71083
+ return
71066
71084
  if senderMatchPattern:
71067
71085
  for message in result['messages']:
71068
71086
  if _checkSenderMatch(message):
71069
71087
  break
71070
71088
  else:
71071
71089
  return
71090
+ k = 0
71072
71091
  for message in result['messages']:
71073
- _printMessage(user, message)
71092
+ k += 1
71093
+ _printMessage(user, message, False)
71094
+ if k == parameters['maxMessagesPerThread']:
71095
+ break
71074
71096
  messageThreadCounts['threads'] += 1
71097
+ parameters['messagesProcessed'] += 1
71075
71098
 
71076
71099
  def _countThreadLabels(user, result):
71077
71100
  for message in result['messages']:
@@ -71086,8 +71109,12 @@ def printShowMessagesThreads(users, entityType):
71086
71109
  else:
71087
71110
  return
71088
71111
  else:
71112
+ k = 0
71089
71113
  for message in result['messages']:
71114
+ k += 1
71090
71115
  messageThreadCounts['size'] += message['sizeEstimate']
71116
+ if k == parameters['maxMessagesPerThread']:
71117
+ break
71091
71118
  messageThreadCounts['threads'] += 1
71092
71119
 
71093
71120
  _GMAIL_ERROR_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST, GAPI.INVALID_MESSAGE_ID: Msg.INVALID_MESSAGE_ID}
@@ -74669,14 +74696,14 @@ def createNotesACLs(users):
74669
74696
  request['parent'] = name
74670
74697
  try:
74671
74698
  permissions = callGAPI(keep.notes().permissions(), 'batchCreate',
74672
- throwReasons=GAPI.KEEP_THROW_REASONS,
74699
+ throwReasons=GAPI.KEEP_THROW_REASONS+[GAPI.FAILED_PRECONDITION],
74673
74700
  parent=name, body=rbody)
74674
74701
  entityNumItemsActionPerformed(entityKVList, kcount, Ent.NOTE_ACL, j, jcount)
74675
74702
  if showDetails:
74676
74703
  Ind.Increment()
74677
74704
  _showNotePermissions(permissions['permissions'])
74678
74705
  Ind.Decrement()
74679
- except (GAPI.badRequest, GAPI.invalidArgument, GAPI.notFound) as e:
74706
+ except (GAPI.badRequest, GAPI.invalidArgument, GAPI.notFound, GAPI.failedPrecondition) as e:
74680
74707
  entityActionFailedWarning(entityKVList, str(e), i, count)
74681
74708
  except GAPI.authError:
74682
74709
  userKeepServiceNotEnabledWarning(user, i, count)
@@ -23,8 +23,10 @@
23
23
  # The following GM_XXX constants are arbitrary but must be unique
24
24
  # Most errors print a message and bail out with a return code
25
25
  # Some commands want to set a non-zero return code but not bail
26
- # GAM admin user
27
- ADMIN = 'admin'
26
+ # GAM admin user from oauth2.txt or oauth2service.json
27
+ ADMIN = 'admn'
28
+ # Drive service for admin; used to look up Shared Drive Names
29
+ ADMIN_DRIVE = 'addr'
28
30
  # Number/length of API call retries
29
31
  API_CALLS_RETRY_DATA = 'rtry'
30
32
  # GAM cache directory. If no_cache is True, this variable will be set to None
@@ -215,6 +217,7 @@ REDIRECT_QUEUE_EOF = 'eof'
215
217
  #
216
218
  Globals = {
217
219
  ADMIN: None,
220
+ ADMIN_DRIVE: None,
218
221
  API_CALLS_RETRY_DATA: {},
219
222
  CACHE_DIR: None,
220
223
  CACHE_DISCOVERY_ONLY: True,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes