gam7 7.10.10__tar.gz → 7.11.0__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 (318) hide show
  1. {gam7-7.10.10 → gam7-7.11.0}/.github/workflows/build.yml +1 -1
  2. {gam7-7.10.10 → gam7-7.11.0}/PKG-INFO +1 -1
  3. {gam7-7.10.10 → gam7-7.11.0}/src/GamCommands.txt +14 -0
  4. {gam7-7.10.10 → gam7-7.11.0}/src/GamUpdate.txt +9 -1
  5. {gam7-7.10.10 → gam7-7.11.0}/src/gam/__init__.py +173 -55
  6. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glclargs.py +3 -0
  7. {gam7-7.10.10 → gam7-7.11.0}/wiki/Classroom-Courses.md +24 -0
  8. {gam7-7.10.10 → gam7-7.11.0}/wiki/GamUpdates.md +14 -0
  9. {gam7-7.10.10 → gam7-7.11.0}/wiki/How-to-Upgrade-Legacy-GAM-to-GAM7.md +2 -2
  10. {gam7-7.10.10 → gam7-7.11.0}/wiki/Shared-Drives.md +10 -2
  11. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Shared-Drives.md +10 -2
  12. {gam7-7.10.10 → gam7-7.11.0}/wiki/Version-and-Help.md +6 -6
  13. {gam7-7.10.10 → gam7-7.11.0}/.github/ISSUE_TEMPLATE/aa-question.md +0 -0
  14. {gam7-7.10.10 → gam7-7.11.0}/.github/ISSUE_TEMPLATE/za-bug-report.md +0 -0
  15. {gam7-7.10.10 → gam7-7.11.0}/.github/ISSUE_TEMPLATE/zz-feature-request.md +0 -0
  16. {gam7-7.10.10 → gam7-7.11.0}/.github/ISSUE_TEMPLATE.txt +0 -0
  17. {gam7-7.10.10 → gam7-7.11.0}/.github/actions/decrypt.sh +0 -0
  18. {gam7-7.10.10 → gam7-7.11.0}/.github/actions/entitlements.plist +0 -0
  19. {gam7-7.10.10 → gam7-7.11.0}/.github/actions/package_exclusions.txt +0 -0
  20. {gam7-7.10.10 → gam7-7.11.0}/.github/stale.yml +0 -0
  21. {gam7-7.10.10 → gam7-7.11.0}/.github/workflows/codeql-analysis.yml +0 -0
  22. {gam7-7.10.10 → gam7-7.11.0}/.github/workflows/get-cacerts.yml +0 -0
  23. {gam7-7.10.10 → gam7-7.11.0}/.github/workflows/pushwiki.yml +0 -0
  24. {gam7-7.10.10 → gam7-7.11.0}/.github/workflows/pypi.yml +0 -0
  25. {gam7-7.10.10 → gam7-7.11.0}/.pre-commit-config.yaml +0 -0
  26. {gam7-7.10.10 → gam7-7.11.0}/LICENSE +0 -0
  27. {gam7-7.10.10 → gam7-7.11.0}/README.md +0 -0
  28. {gam7-7.10.10 → gam7-7.11.0}/pyproject.toml +0 -0
  29. {gam7-7.10.10 → gam7-7.11.0}/src/.gitignore +0 -0
  30. {gam7-7.10.10 → gam7-7.11.0}/src/LICENSE +0 -0
  31. {gam7-7.10.10 → gam7-7.11.0}/src/cacerts.pem +0 -0
  32. {gam7-7.10.10 → gam7-7.11.0}/src/callgam.py +0 -0
  33. {gam7-7.10.10 → gam7-7.11.0}/src/gam/__main__.py +0 -0
  34. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/__init__.py +0 -0
  35. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/auth.py +0 -0
  36. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/client.py +0 -0
  37. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/core.py +0 -0
  38. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/data.py +0 -0
  39. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/http.py +0 -0
  40. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/http_core.py +0 -0
  41. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/http_interface.py +0 -0
  42. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/mock_http.py +0 -0
  43. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/mock_http_core.py +0 -0
  44. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/mock_service.py +0 -0
  45. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/service.py +0 -0
  46. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/token_store.py +0 -0
  47. {gam7-7.10.10 → gam7-7.11.0}/src/gam/atom/url.py +0 -0
  48. {gam7-7.10.10 → gam7-7.11.0}/src/gam/cacerts.pem +0 -0
  49. {gam7-7.10.10 → gam7-7.11.0}/src/gam/cbcm-v1.1beta1.json +0 -0
  50. {gam7-7.10.10 → gam7-7.11.0}/src/gam/contactdelegation-v1.json +0 -0
  51. {gam7-7.10.10 → gam7-7.11.0}/src/gam/datastudio-v1.json +0 -0
  52. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/__init__.py +0 -0
  53. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glaction.py +0 -0
  54. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glapi.py +0 -0
  55. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glcfg.py +0 -0
  56. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glentity.py +0 -0
  57. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glgapi.py +0 -0
  58. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glgdata.py +0 -0
  59. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glglobals.py +0 -0
  60. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glindent.py +0 -0
  61. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glmsgs.py +0 -0
  62. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glskus.py +0 -0
  63. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/gluprop.py +0 -0
  64. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/glverlibs.py +0 -0
  65. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gamlib/yubikey.py +0 -0
  66. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/__init__.py +0 -0
  67. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/alt/__init__.py +0 -0
  68. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/alt/app_engine.py +0 -0
  69. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/alt/appengine.py +0 -0
  70. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/__init__.py +0 -0
  71. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/audit/__init__.py +0 -0
  72. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/audit/service.py +0 -0
  73. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/contacts/__init__.py +0 -0
  74. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/contacts/service.py +0 -0
  75. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/apps/service.py +0 -0
  76. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/service.py +0 -0
  77. {gam7-7.10.10 → gam7-7.11.0}/src/gam/gdata/urlfetch.py +0 -0
  78. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/__init__.py +0 -0
  79. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/_auth.py +0 -0
  80. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/_helpers.py +0 -0
  81. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/channel.py +0 -0
  82. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/discovery.py +0 -0
  83. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/discovery_cache/__init__.py +0 -0
  84. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/discovery_cache/appengine_memcache.py +0 -0
  85. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/discovery_cache/base.py +0 -0
  86. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/discovery_cache/file_cache.py +0 -0
  87. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/errors.py +0 -0
  88. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/http.py +0 -0
  89. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/mimeparse.py +0 -0
  90. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/model.py +0 -0
  91. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/schema.py +0 -0
  92. {gam7-7.10.10 → gam7-7.11.0}/src/gam/googleapiclient/version.py +0 -0
  93. {gam7-7.10.10 → gam7-7.11.0}/src/gam/iso8601/__init__.py +0 -0
  94. {gam7-7.10.10 → gam7-7.11.0}/src/gam/iso8601/iso8601.py +0 -0
  95. {gam7-7.10.10 → gam7-7.11.0}/src/gam/meet-v2beta.json +0 -0
  96. {gam7-7.10.10 → gam7-7.11.0}/src/gam/serviceaccountlookup-v1.json +0 -0
  97. {gam7-7.10.10 → gam7-7.11.0}/src/gam/six.py +0 -0
  98. {gam7-7.10.10 → gam7-7.11.0}/src/gam-install.sh +0 -0
  99. {gam7-7.10.10 → gam7-7.11.0}/src/gam-setup.bat +0 -0
  100. {gam7-7.10.10 → gam7-7.11.0}/src/gam.exe.manifest +0 -0
  101. {gam7-7.10.10 → gam7-7.11.0}/src/gam.py +0 -0
  102. {gam7-7.10.10 → gam7-7.11.0}/src/gam.spec +0 -0
  103. {gam7-7.10.10 → gam7-7.11.0}/src/gam.wxs +0 -0
  104. {gam7-7.10.10 → gam7-7.11.0}/src/license.rtf +0 -0
  105. {gam7-7.10.10 → gam7-7.11.0}/src/requirements-dev.txt +0 -0
  106. {gam7-7.10.10 → gam7-7.11.0}/src/requirements.txt +0 -0
  107. {gam7-7.10.10 → gam7-7.11.0}/src/setup.cfg +0 -0
  108. {gam7-7.10.10 → gam7-7.11.0}/src/setup.py +0 -0
  109. {gam7-7.10.10 → gam7-7.11.0}/src/tools/a_atleast_b.py +0 -0
  110. {gam7-7.10.10 → gam7-7.11.0}/src/tools/gen-wix-xml-filelist.py +0 -0
  111. {gam7-7.10.10 → gam7-7.11.0}/src/tools/mkGamRef.py +0 -0
  112. {gam7-7.10.10 → gam7-7.11.0}/src/tools/openssl.props +0 -0
  113. {gam7-7.10.10 → gam7-7.11.0}/src/version_info.txt.in +0 -0
  114. {gam7-7.10.10 → gam7-7.11.0}/wiki/00scratch.md +0 -0
  115. {gam7-7.10.10 → gam7-7.11.0}/wiki/Addresses.md +0 -0
  116. {gam7-7.10.10 → gam7-7.11.0}/wiki/Administrators.md +0 -0
  117. {gam7-7.10.10 → gam7-7.11.0}/wiki/Alert-Center.md +0 -0
  118. {gam7-7.10.10 → gam7-7.11.0}/wiki/Aliases.md +0 -0
  119. {gam7-7.10.10 → gam7-7.11.0}/wiki/Authorization.md +0 -0
  120. {gam7-7.10.10 → gam7-7.11.0}/wiki/BNF-Syntax.md +0 -0
  121. {gam7-7.10.10 → gam7-7.11.0}/wiki/Basic-Items.md +0 -0
  122. {gam7-7.10.10 → gam7-7.11.0}/wiki/Bulk-Processing.md +0 -0
  123. {gam7-7.10.10 → gam7-7.11.0}/wiki/CSV-Input-Filtering.md +0 -0
  124. {gam7-7.10.10 → gam7-7.11.0}/wiki/CSV-Output-Filtering.md +0 -0
  125. {gam7-7.10.10 → gam7-7.11.0}/wiki/CSV-Special-Characters.md +0 -0
  126. {gam7-7.10.10 → gam7-7.11.0}/wiki/Calendars-Access.md +0 -0
  127. {gam7-7.10.10 → gam7-7.11.0}/wiki/Calendars-Events.md +0 -0
  128. {gam7-7.10.10 → gam7-7.11.0}/wiki/Calendars.md +0 -0
  129. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chat-Bot.md +0 -0
  130. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-AUE-Counts.md +0 -0
  131. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Browser-Cloud-Management.md +0 -0
  132. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Installed-Apps.md +0 -0
  133. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Needs-Attention-Counts.md +0 -0
  134. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Policies.md +0 -0
  135. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Printers.md +0 -0
  136. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Profile-Management.md +0 -0
  137. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Version-Counts.md +0 -0
  138. {gam7-7.10.10 → gam7-7.11.0}/wiki/Chrome-Version-History.md +0 -0
  139. {gam7-7.10.10 → gam7-7.11.0}/wiki/ChromeOS-Devices.md +0 -0
  140. {gam7-7.10.10 → gam7-7.11.0}/wiki/Classroom-Guardians.md +0 -0
  141. {gam7-7.10.10 → gam7-7.11.0}/wiki/Classroom-Invitations.md +0 -0
  142. {gam7-7.10.10 → gam7-7.11.0}/wiki/Classroom-Membership.md +0 -0
  143. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Channel.md +0 -0
  144. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Identity-Devices.md +0 -0
  145. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Identity-Groups-Membership.md +0 -0
  146. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Identity-Groups.md +0 -0
  147. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Identity-Policies.md +0 -0
  148. {gam7-7.10.10 → gam7-7.11.0}/wiki/Cloud-Storage.md +0 -0
  149. {gam7-7.10.10 → gam7-7.11.0}/wiki/Collections-of-ChromeOS-Devices.md +0 -0
  150. {gam7-7.10.10 → gam7-7.11.0}/wiki/Collections-of-Items.md +0 -0
  151. {gam7-7.10.10 → gam7-7.11.0}/wiki/Collections-of-Users.md +0 -0
  152. {gam7-7.10.10 → gam7-7.11.0}/wiki/Command-Data-From-Google-Docs-Sheets-Storage.md +0 -0
  153. {gam7-7.10.10 → gam7-7.11.0}/wiki/Command-Line-Parsing.md +0 -0
  154. {gam7-7.10.10 → gam7-7.11.0}/wiki/Command-Logging-Progress.md +0 -0
  155. {gam7-7.10.10 → gam7-7.11.0}/wiki/Context-Aware-Access-Levels.md +0 -0
  156. {gam7-7.10.10 → gam7-7.11.0}/wiki/Customer.md +0 -0
  157. {gam7-7.10.10 → gam7-7.11.0}/wiki/Domain-People-Contacts-Profiles.md +0 -0
  158. {gam7-7.10.10 → gam7-7.11.0}/wiki/Domain-SharedContacts.md +0 -0
  159. {gam7-7.10.10 → gam7-7.11.0}/wiki/Domains-Verification.md +0 -0
  160. {gam7-7.10.10 → gam7-7.11.0}/wiki/Domains.md +0 -0
  161. {gam7-7.10.10 → gam7-7.11.0}/wiki/Downloads-Installs.md +0 -0
  162. {gam7-7.10.10 → gam7-7.11.0}/wiki/Drive-File-Selection.md +0 -0
  163. {gam7-7.10.10 → gam7-7.11.0}/wiki/Drive-Items.md +0 -0
  164. {gam7-7.10.10 → gam7-7.11.0}/wiki/Drive-REST-API-v3.md +0 -0
  165. {gam7-7.10.10 → gam7-7.11.0}/wiki/Email-Audit-Monitor.md +0 -0
  166. {gam7-7.10.10 → gam7-7.11.0}/wiki/Find-File-Owner.md +0 -0
  167. {gam7-7.10.10 → gam7-7.11.0}/wiki/GAM-Public-Chat-Room.md +0 -0
  168. {gam7-7.10.10 → gam7-7.11.0}/wiki/GAM-Return-Codes.md +0 -0
  169. {gam7-7.10.10 → gam7-7.11.0}/wiki/GAM-with-minimal-GCP-rights.md +0 -0
  170. {gam7-7.10.10 → gam7-7.11.0}/wiki/GAM7-on-Android-Devices.md +0 -0
  171. {gam7-7.10.10 → gam7-7.11.0}/wiki/GAM7-on-Chrome-OS-Devices.md +0 -0
  172. {gam7-7.10.10 → gam7-7.11.0}/wiki/Google-Data-Transfers.md +0 -0
  173. {gam7-7.10.10 → gam7-7.11.0}/wiki/Google-Network-Addresses.md +0 -0
  174. {gam7-7.10.10 → gam7-7.11.0}/wiki/Groups-Membership.md +0 -0
  175. {gam7-7.10.10 → gam7-7.11.0}/wiki/Groups.md +0 -0
  176. {gam7-7.10.10 → gam7-7.11.0}/wiki/HTTPS-Proxy.md +0 -0
  177. {gam7-7.10.10 → gam7-7.11.0}/wiki/Home.md +0 -0
  178. {gam7-7.10.10 → gam7-7.11.0}/wiki/How-to-Install-GAM7.md +0 -0
  179. {gam7-7.10.10 → gam7-7.11.0}/wiki/How-to-Uninstall-GAM7.md +0 -0
  180. {gam7-7.10.10 → gam7-7.11.0}/wiki/How-to-Update-GAM7.md +0 -0
  181. {gam7-7.10.10 → gam7-7.11.0}/wiki/How-to-Upgrade-GAMADV-XTD3-to-GAM7.md +0 -0
  182. {gam7-7.10.10 → gam7-7.11.0}/wiki/Inbound-SSO.md +0 -0
  183. {gam7-7.10.10 → gam7-7.11.0}/wiki/Install-GAM-as-Python-Library.md +0 -0
  184. {gam7-7.10.10 → gam7-7.11.0}/wiki/Licenses.md +0 -0
  185. {gam7-7.10.10 → gam7-7.11.0}/wiki/List-Items.md +0 -0
  186. {gam7-7.10.10 → gam7-7.11.0}/wiki/List.md +0 -0
  187. {gam7-7.10.10 → gam7-7.11.0}/wiki/Meta-Commands-and-File-Redirection.md +0 -0
  188. {gam7-7.10.10 → gam7-7.11.0}/wiki/Mobile-Devices.md +0 -0
  189. {gam7-7.10.10 → gam7-7.11.0}/wiki/Organizational-Units.md +0 -0
  190. {gam7-7.10.10 → gam7-7.11.0}/wiki/Other-Resources.md +0 -0
  191. {gam7-7.10.10 → gam7-7.11.0}/wiki/Permission-Matches.md +0 -0
  192. {gam7-7.10.10 → gam7-7.11.0}/wiki/Python-Regular-Expressions.md +0 -0
  193. {gam7-7.10.10 → gam7-7.11.0}/wiki/README.md +0 -0
  194. {gam7-7.10.10 → gam7-7.11.0}/wiki/Rclone.md +0 -0
  195. {gam7-7.10.10 → gam7-7.11.0}/wiki/Reports.md +0 -0
  196. {gam7-7.10.10 → gam7-7.11.0}/wiki/Reseller.md +0 -0
  197. {gam7-7.10.10 → gam7-7.11.0}/wiki/Resources.md +0 -0
  198. {gam7-7.10.10 → gam7-7.11.0}/wiki/Running-GAM7-securely-on-a-Google-Compute-Engine.md +0 -0
  199. {gam7-7.10.10 → gam7-7.11.0}/wiki/SSL-Root-CA-Certificates.md +0 -0
  200. {gam7-7.10.10 → gam7-7.11.0}/wiki/Schemas.md +0 -0
  201. {gam7-7.10.10 → gam7-7.11.0}/wiki/Scripts.md +0 -0
  202. {gam7-7.10.10 → gam7-7.11.0}/wiki/Send-Email.md +0 -0
  203. {gam7-7.10.10 → gam7-7.11.0}/wiki/Sites.md +0 -0
  204. {gam7-7.10.10 → gam7-7.11.0}/wiki/Tag-Replace.md +0 -0
  205. {gam7-7.10.10 → gam7-7.11.0}/wiki/Todrive.md +0 -0
  206. {gam7-7.10.10 → gam7-7.11.0}/wiki/Unmanaged-Accounts.md +0 -0
  207. {gam7-7.10.10 → gam7-7.11.0}/wiki/Upgrade-Benefits.md +0 -0
  208. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Analytics-Admin.md +0 -0
  209. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Application-Specific-Passwords.md +0 -0
  210. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Backup-Verification-Codes.md +0 -0
  211. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Calendars-Access.md +0 -0
  212. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Calendars-Events.md +0 -0
  213. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Calendars.md +0 -0
  214. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Chat.md +0 -0
  215. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Classification-Labels.md +0 -0
  216. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Classroom-Profile.md +0 -0
  217. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Contacts-Delegates.md +0 -0
  218. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Contacts.md +0 -0
  219. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Deprovision.md +0 -0
  220. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Activity-Settings.md +0 -0
  221. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Cleanup.md +0 -0
  222. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Comments.md +0 -0
  223. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Copy-Move.md +0 -0
  224. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Files-Display.md +0 -0
  225. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Files-Manage.md +0 -0
  226. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Orphans.md +0 -0
  227. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Ownership.md +0 -0
  228. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Permissions.md +0 -0
  229. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Query.md +0 -0
  230. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Revisions.md +0 -0
  231. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Shortcuts.md +0 -0
  232. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Drive-Transfer.md +0 -0
  233. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Forms.md +0 -0
  234. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-CSE.md +0 -0
  235. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Delegates.md +0 -0
  236. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Filters.md +0 -0
  237. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Forwarding.md +0 -0
  238. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Labels.md +0 -0
  239. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Messages-Threads.md +0 -0
  240. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Profile.md +0 -0
  241. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-S-MIME.md +0 -0
  242. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Send-As-Signature-Vacation.md +0 -0
  243. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Gmail-Settings.md +0 -0
  244. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Group-Membership.md +0 -0
  245. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Keep-Notes.md +0 -0
  246. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Looker-Studio.md +0 -0
  247. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Meet.md +0 -0
  248. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-People-Contacts-Profiles.md +0 -0
  249. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Photo.md +0 -0
  250. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Profile-Photo.md +0 -0
  251. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Profile-Sharing.md +0 -0
  252. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Signout-Turnoff2SV.md +0 -0
  253. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Spreadsheets.md +0 -0
  254. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Tasks.md +0 -0
  255. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-Tokens.md +0 -0
  256. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users-YouTube.md +0 -0
  257. {gam7-7.10.10 → gam7-7.11.0}/wiki/Users.md +0 -0
  258. {gam7-7.10.10 → gam7-7.11.0}/wiki/Using-GAM7-with-a-YubiKey.md +0 -0
  259. {gam7-7.10.10 → gam7-7.11.0}/wiki/Using-GAM7-with-a-delegated-admin-service-account.md +0 -0
  260. {gam7-7.10.10 → gam7-7.11.0}/wiki/Vault-Takeout.md +0 -0
  261. {gam7-7.10.10 → gam7-7.11.0}/wiki/Verifying-a-GAM7-Build-is-Legitimate-and-Official.md +0 -0
  262. {gam7-7.10.10 → gam7-7.11.0}/wiki/_Sidebar.md +0 -0
  263. {gam7-7.10.10 → gam7-7.11.0}/wiki/gam.cfg.md +0 -0
  264. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-AdminSettingsExamples.md +0 -0
  265. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Android-Installation.md +0 -0
  266. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-BulkOperations.md +0 -0
  267. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-CalendarExamples.md +0 -0
  268. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Chat-Bot.md +0 -0
  269. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Chrome-Browser-Management.md +0 -0
  270. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Chrome-OS-Installation.md +0 -0
  271. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Chrome-Policy-Settings.md +0 -0
  272. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Cloud-Identity-Groups.md +0 -0
  273. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Calendar-Resources.md +0 -0
  274. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Calendars.md +0 -0
  275. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Definitions.md +0 -0
  276. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Drive.md +0 -0
  277. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Email.md +0 -0
  278. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Group-Attributes.md +0 -0
  279. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Groups.md +0 -0
  280. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Command-Reference-Users.md +0 -0
  281. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Context-Aware-Access-Levels.md +0 -0
  282. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Creating-client_secrets.json-and-oauth2service.json.md +0 -0
  283. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-CreatingClientSecretsFile.md +0 -0
  284. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Custom-Schemas.md +0 -0
  285. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Data-Transfers.md +0 -0
  286. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-DomainVerification.md +0 -0
  287. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-ExamplesAccountAuditing.md +0 -0
  288. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-ExamplesCSV.md +0 -0
  289. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-ExamplesEmailSettings.md +0 -0
  290. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-ExamplesOrganizations.md +0 -0
  291. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM-Discussion-Group.md +0 -0
  292. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM-on-Android-and-Chrome-OS.md +0 -0
  293. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM-options-files.md +0 -0
  294. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM-with--minimal-GCP-rights.md +0 -0
  295. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM3CSVListings.md +0 -0
  296. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM3DirectoryCommands.md +0 -0
  297. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM3GroupSettings.md +0 -0
  298. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GAM7-FAQ.md +0 -0
  299. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Google-Vault---Takeout-Commands.md +0 -0
  300. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-GoogleDriveManagement.md +0 -0
  301. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Grouping-18-or-Older-Users.md +0 -0
  302. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Home.md +0 -0
  303. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-How-to-upgrade-from-Standard-GAM.md +0 -0
  304. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Inbound-SSO-Settings.md +0 -0
  305. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-LicenseExamples.md +0 -0
  306. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Managing-Admins.md +0 -0
  307. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Managing-CloudPrint-Printers.md +0 -0
  308. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Managing-Devices.md +0 -0
  309. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Managing-Google-Classroom.md +0 -0
  310. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-OAuthKeyManagement.md +0 -0
  311. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Printers.md +0 -0
  312. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-ResellerCommands.md +0 -0
  313. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Running-GAM-on-Google-Compute-Engine-(GCE)-Securely.md +0 -0
  314. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-SecurityExamples.md +0 -0
  315. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-UnmanagedUsersExamples.md +0 -0
  316. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Use-a-Yubikey.md +0 -0
  317. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-Using-GAM-with-a-delegated-admin-service-account-(DASA).md +0 -0
  318. {gam7-7.10.10 → gam7-7.11.0}/wiki/l-_Footer.md +0 -0
@@ -126,7 +126,7 @@ jobs:
126
126
  with:
127
127
  path: |
128
128
  cache.tar.xz
129
- key: gam-${{ matrix.jid }}-20250611
129
+ key: gam-${{ matrix.jid }}-20250701
130
130
 
131
131
  - name: Untar Cache archive
132
132
  if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gam7
3
- Version: 7.10.10
3
+ Version: 7.11.0
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
@@ -3120,8 +3120,15 @@ gam delete courses <CourseEntity> [archive|archived]
3120
3120
  gam course <CourseID> create|add alias <CourseAlias>
3121
3121
  gam course <CourseID> delete alias <CourseAlias>
3122
3122
 
3123
+ gam course <CourseID> create announcement
3124
+ text <String> [scheduledtime <Time>] [state draft|published]
3125
+ gam course <CourseID> remove announcement <CourseAnnouncementID>
3126
+ gam course <CourseID> update announcement <CourseAnnouncementID>
3127
+ [text <String>] [scheduledtime <Time>] [state published]
3128
+
3123
3129
  gam course <CourseID> create|add topic <CourseTopic>
3124
3130
  gam course <CourseID> delete topic <CourseTopicID>
3131
+ gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
3125
3132
 
3126
3133
  gam course <CourseID> create|add teachers [makefirstteacherowner] <UserItem>
3127
3134
  gam course <CourseID> create|add students <UserItem>
@@ -3133,8 +3140,15 @@ gam course <CourseID> sync students [addonly|removeonly] <UserTypeEntity>
3133
3140
  gam courses <CourseEntity> create|add alias <CourseAliasEntity>
3134
3141
  gam courses <CourseEntity> delete alias <CourseAliasEntity>
3135
3142
 
3143
+ gam courses <CourseEntity> create announcement
3144
+ text <String> [scheduledtime <Time>] [state draft|published]
3145
+ gam courses <CourseEntity> remove announcement <CourseAnnouncementIDEntity>
3146
+ gam courses <CourseEntity> update announcement <CourseAnnouncemntIDEntity>
3147
+ [text <String>] [scheduledtime <Time>] [state published]
3148
+
3136
3149
  gam courses <CourseEntity> create|add topic <CourseTopicEntity>
3137
3150
  gam courses <CourseEntity> delete topic <CourseTopicIDEntity>
3151
+ gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
3138
3152
 
3139
3153
  gam courses <CourseEntity> create|add teachers [makefirstteacherowner] <UserTypeEntity>
3140
3154
  gam courses <CourseEntity> create|add students <UserTypeEntity>
@@ -1,8 +1,16 @@
1
+ 7.11.00
2
+
3
+ Added commands to manage classroom/course announcements.
4
+
5
+ * See: https://github.com/GAM-team/GAM/wiki/Classroom-Courses#manage-course-announcements
6
+
7
+ Upgraded to OpenSSL 3.5.1.
8
+
1
9
  7.10.10
2
10
 
3
11
  Added choices `text` and `hyperlink` to option `showwebviewlink` in `gam [<UserTypeEntity>] print|show shareddrives`.
4
12
  * `showwebviewlink text` - Displays `https://drive.google.com/drive/folders/<SharedDriveID>`
5
- * `showwebviewlink hyperlink` - Dsiplays `=HYPERLINK("https://drive.google.com/drive/folders/<SharedDriveID>", "<SharedDriveNamw>")
13
+ * `showwebviewlink hyperlink` - Displays `=HYPERLINK("https://drive.google.com/drive/folders/<SharedDriveID>", "<SharedDriveName>")`
6
14
 
7
15
  7.10.09
8
16
 
@@ -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.10.10'
28
+ __version__ = '7.11.00'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
@@ -46776,6 +46776,13 @@ COURSE_STATE_MAPS = {
46776
46776
  'published': 'PUBLISHED',
46777
46777
  'deleted': 'DELETED',
46778
46778
  },
46779
+ Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST: {
46780
+ 'draft': 'DRAFT',
46781
+ 'published': 'PUBLISHED',
46782
+ },
46783
+ Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST: {
46784
+ 'published': 'PUBLISHED',
46785
+ },
46779
46786
  Cmd.OB_COURSE_WORK_STATE_LIST: {
46780
46787
  'draft': 'DRAFT',
46781
46788
  'published': 'PUBLISHED',
@@ -48923,7 +48930,7 @@ def doPrintCourseParticipants():
48923
48930
  csvPF.SetSortTitles(COURSE_PARTICIPANTS_SORT_TITLES)
48924
48931
  csvPF.writeCSVfile('Course Participants')
48925
48932
 
48926
- def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48933
+ def _batchAddItemsToCourse(croom, courseId, i, count, addItems, addType):
48927
48934
  _ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
48928
48935
  GAPI.ALREADY_EXISTS: Msg.DUPLICATE,
48929
48936
  GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED}
@@ -48939,7 +48946,7 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48939
48946
  else:
48940
48947
  errMsg = getHTTPError(_ADD_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
48941
48948
  if (reason == GAPI.PERMISSION_DENIED) and (ri[RI_ROLE] in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
48942
- errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} role {Ent.Singular(ri[RI_ROLE])}'
48949
+ errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} addType {Ent.Singular(ri[RI_ROLE])}'
48943
48950
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
48944
48951
  return
48945
48952
  waitOnFailure(1, 10, reason, message)
@@ -48961,39 +48968,44 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48961
48968
  except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
48962
48969
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
48963
48970
 
48964
- if role == Ent.STUDENT:
48971
+ if addType == Ent.STUDENT:
48965
48972
  service = croom.courses().students()
48966
48973
  attribute = 'userId'
48967
- elif role == Ent.TEACHER:
48974
+ elif addType == Ent.TEACHER:
48968
48975
  service = croom.courses().teachers()
48969
48976
  attribute = 'userId'
48970
- elif role == Ent.COURSE_ALIAS:
48977
+ elif addType == Ent.COURSE_ALIAS:
48971
48978
  service = croom.courses().aliases()
48972
48979
  attribute = 'alias'
48973
- else: # role == Ent.COURSE_TOPIC:
48980
+ elif addType == Ent.COURSE_TOPIC:
48974
48981
  service = croom.courses().topics()
48975
48982
  attribute = 'name'
48983
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
48984
+ service = croom.courses().announcements()
48985
+ attribute = 'text'
48976
48986
  method = getattr(service, 'create')
48977
48987
  Act.Set(Act.ADD)
48978
- jcount = len(addParticipants)
48988
+ jcount = len(addItems)
48979
48989
  noScopeCourseId = removeCourseIdScope(courseId)
48980
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
48990
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, addType, i, count)
48981
48991
  Ind.Increment()
48982
48992
  svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', '')]+GM.Globals[GM.EXTRA_ARGS_LIST])
48983
48993
  dbatch = croom.new_batch_http_request(callback=_callbackAddItemsToCourse)
48984
48994
  bcount = 0
48985
48995
  j = 0
48986
- for participant in addParticipants:
48996
+ for addItem in addItems:
48987
48997
  j += 1
48988
48998
  svcparms = svcargs.copy()
48989
- if role in {Ent.STUDENT, Ent.TEACHER}:
48990
- svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
48991
- elif role == Ent.COURSE_ALIAS:
48992
- svcparms['body'][attribute] = addCourseAliasScope(participant)
48999
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49000
+ svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(addItem)
49001
+ elif addType == Ent.COURSE_ALIAS:
49002
+ svcparms['body'][attribute] = addCourseAliasScope(addItem)
48993
49003
  cleanItem = removeCourseAliasScope(svcparms['body'][attribute])
48994
- else: # role == Ent.COURSE_TOPIC:
48995
- svcparms['body'][attribute] = cleanItem = participant
48996
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49004
+ elif addType == Ent.COURSE_TOPIC:
49005
+ svcparms['body'][attribute] = cleanItem = addItem
49006
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49007
+ svcparms['body'] = cleanItem = addItem
49008
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, addType))
48997
49009
  bcount += 1
48998
49010
  if bcount >= GC.Values[GC.BATCH_SIZE]:
48999
49011
  executeBatch(dbatch)
@@ -49003,7 +49015,7 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
49003
49015
  dbatch.execute()
49004
49016
  Ind.Decrement()
49005
49017
 
49006
- def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, role):
49018
+ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeItems, removeType):
49007
49019
  _REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
49008
49020
  GAPI.FORBIDDEN: Msg.FORBIDDEN,
49009
49021
  GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED}
@@ -49024,7 +49036,7 @@ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, r
49024
49036
  try:
49025
49037
  callGAPI(service, 'delete',
49026
49038
  throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49027
- GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49039
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FAILED_PRECONDITION],
49028
49040
  retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
49029
49041
  courseId=addCourseIdScope(ri[RI_ENTITY]),
49030
49042
  body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
@@ -49035,42 +49047,47 @@ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, r
49035
49047
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49036
49048
  except GAPI.permissionDenied:
49037
49049
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49038
- except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49050
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable, GAPI.failedPrecondition) as e:
49039
49051
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49040
49052
 
49041
- if role == Ent.STUDENT:
49053
+ if removeType == Ent.STUDENT:
49042
49054
  service = croom.courses().students()
49043
49055
  attribute = 'userId'
49044
- elif role == Ent.TEACHER:
49056
+ elif removeType == Ent.TEACHER:
49045
49057
  service = croom.courses().teachers()
49046
49058
  attribute = 'userId'
49047
- elif role == Ent.COURSE_ALIAS:
49059
+ elif removeType == Ent.COURSE_ALIAS:
49048
49060
  service = croom.courses().aliases()
49049
49061
  attribute = 'alias'
49050
- else: # role == Ent.COURSE_TOPIC:
49062
+ elif removeType == Ent.COURSE_TOPIC:
49051
49063
  service = croom.courses().topics()
49052
49064
  attribute = 'id'
49065
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49066
+ service = croom.courses().announcements()
49067
+ attribute = 'id'
49053
49068
  method = getattr(service, 'delete')
49054
49069
  Act.Set(Act.REMOVE)
49055
- jcount = len(removeParticipants)
49070
+ jcount = len(removeItems)
49056
49071
  noScopeCourseId = removeCourseIdScope(courseId)
49057
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
49072
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, removeType, i, count)
49058
49073
  Ind.Increment()
49059
49074
  svcargs = dict([('courseId', courseId), ('fields', ''), (attribute, None)]+GM.Globals[GM.EXTRA_ARGS_LIST])
49060
49075
  dbatch = croom.new_batch_http_request(callback=_callbackRemoveItemsFromCourse)
49061
49076
  bcount = 0
49062
49077
  j = 0
49063
- for participant in removeParticipants:
49078
+ for removeItem in removeItems:
49064
49079
  j += 1
49065
49080
  svcparms = svcargs.copy()
49066
- if role in {Ent.STUDENT, Ent.TEACHER}:
49067
- svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
49068
- elif role == Ent.COURSE_ALIAS:
49069
- svcparms[attribute] = addCourseAliasScope(participant)
49081
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49082
+ svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(removeItem)
49083
+ elif removeType == Ent.COURSE_ALIAS:
49084
+ svcparms[attribute] = addCourseAliasScope(removeItem)
49070
49085
  cleanItem = removeCourseAliasScope(svcparms[attribute])
49071
- else: # role == Ent.COURSE_TOPIC:
49072
- svcparms[attribute] = cleanItem = participant
49073
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49086
+ elif removeType == Ent.COURSE_TOPIC:
49087
+ svcparms[attribute] = cleanItem = removeItem
49088
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49089
+ svcparms[attribute] = cleanItem = removeItem
49090
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, removeType))
49074
49091
  bcount += 1
49075
49092
  if bcount >= GC.Values[GC.BATCH_SIZE]:
49076
49093
  executeBatch(dbatch)
@@ -49100,9 +49117,28 @@ def _updateCourseOwner(croom, courseId, owner, i, count):
49100
49117
  entityActionPerformedMessage([Ent.COURSE, removeCourseIdScope(courseId), Ent.TEACHER, owner], Msg.ALREADY_WAS_OWNER, i, count)
49101
49118
  Act.Set(action)
49102
49119
 
49103
- ADD_REMOVE_PARTICIPANT_TYPES_MAP = {
49120
+ def getCourseAnnouncement(createCmd):
49121
+ body = {}
49122
+ while Cmd.ArgumentsRemaining():
49123
+ myarg = getArgument()
49124
+ if myarg == 'text':
49125
+ body['text'] = getString(Cmd.OB_STRING, minLen=1, maxLen=30000)
49126
+ elif myarg == 'scheduledtime':
49127
+ body['scheduledTime'] = getTimeOrDeltaFromNow()
49128
+ elif myarg == 'state':
49129
+ body['state'] = getChoice(COURSE_STATE_MAPS[Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST if createCmd else Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST],
49130
+ mapChoice=True)
49131
+ else:
49132
+ unknownArgumentExit()
49133
+ if createCmd and 'text' not in body:
49134
+ missingArgumentExit('text <String>')
49135
+ return body
49136
+
49137
+ ADD_REMOVE_UPDATE_ITEM_TYPES_MAP = {
49104
49138
  'alias': Ent.COURSE_ALIAS,
49105
49139
  'aliases': Ent.COURSE_ALIAS,
49140
+ 'announcement': Ent.COURSE_ANNOUNCEMENT,
49141
+ 'announcements': Ent.COURSE_ANNOUNCEMENT,
49106
49142
  'student': Ent.STUDENT,
49107
49143
  'students': Ent.STUDENT,
49108
49144
  'teacher': Ent.TEACHER,
@@ -49123,6 +49159,10 @@ PARTICIPANT_EN_MAP = {
49123
49159
 
49124
49160
  # gam courses <CourseEntity> create alias <CourseAliasEntity>
49125
49161
  # gam course <CourseID> create alias <CourseAlias>
49162
+ # gam courses <CourseEntity> create announcement
49163
+ # text <String> [scheduledtime <Time>] [state draft|published]
49164
+ # gam course <CourseID> create announcement
49165
+ # text <String> [scheduledtime <Time>] [state draft|published]
49126
49166
  # gam courses <CourseEntity> create topic <CourseTopicEntity>
49127
49167
  # gam course <CourseID> create topic <CourseTopic>
49128
49168
  # gam courses <CourseEntity> create students <UserTypeEntity>
@@ -49131,35 +49171,39 @@ PARTICIPANT_EN_MAP = {
49131
49171
  # gam course <CourseID> create teacher [makefirstteacherowner] <EmailAddress>
49132
49172
  def doCourseAddItems(courseIdList, getEntityListArg):
49133
49173
  croom = buildGAPIObject(API.CLASSROOM)
49134
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49135
- if role == Ent.TEACHER:
49174
+ addType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49175
+ if addType == Ent.TEACHER:
49136
49176
  makeFirstTeacherOwner = checkArgumentPresent(['makefirstteacherowner'])
49137
49177
  else:
49138
49178
  makeFirstTeacherOwner = False
49139
49179
  if not getEntityListArg:
49140
- if role in {Ent.STUDENT, Ent.TEACHER}:
49180
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49141
49181
  addItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49142
- elif role == Ent.COURSE_ALIAS:
49182
+ elif addType == Ent.COURSE_ALIAS:
49143
49183
  addItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49144
- else: # role == Ent.COURSE_TOPIC:
49184
+ elif addType == Ent.COURSE_TOPIC:
49145
49185
  addItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC)
49186
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49187
+ addItems = [getCourseAnnouncement(True)]
49146
49188
  courseParticipantLists = None
49147
49189
  else:
49148
- if role in {Ent.STUDENT, Ent.TEACHER}:
49190
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49149
49191
  _, addItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49150
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]},
49192
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[addType]},
49151
49193
  isSuspended=False, isArchived=False)
49152
- elif role == Ent.COURSE_ALIAS:
49194
+ elif addType == Ent.COURSE_ALIAS:
49153
49195
  addItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49154
- else: # role == Ent.COURSE_TOPIC:
49196
+ elif addType == Ent.COURSE_TOPIC:
49155
49197
  addItems = getEntityList(Cmd.OB_COURSE_TOPIC_ENTITY, shlexSplit=True)
49198
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49199
+ addItems = getCourseAnnouncement(True)
49156
49200
  courseParticipantLists = addItems if isinstance(addItems, dict) else None
49157
49201
  if courseParticipantLists is None:
49158
49202
  firstTeacher = None
49159
49203
  if makeFirstTeacherOwner and addItems:
49160
49204
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49161
49205
  checkForExtraneousArguments()
49162
- i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, role == Ent.COURSE_TOPIC,
49206
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, addType in {Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT},
49163
49207
  addCIIdScope=courseParticipantLists is None)
49164
49208
  for courseId, courseInfo in coursesInfo.items():
49165
49209
  i += 1
@@ -49169,43 +49213,51 @@ def doCourseAddItems(courseIdList, getEntityListArg):
49169
49213
  if makeFirstTeacherOwner and addItems:
49170
49214
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49171
49215
  courseId = addCourseIdScope(courseId)
49172
- _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, role)
49216
+ _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, addType)
49173
49217
  if makeFirstTeacherOwner and firstTeacher:
49174
49218
  _updateCourseOwner(courseInfo['croom'], courseId, firstTeacher, i, count)
49175
49219
 
49176
49220
  # gam courses <CourseEntity> remove alias <CourseAliasEntity>
49177
49221
  # gam course <CourseID> remove alias <CourseAlias>
49222
+ # gam courses <CourseEntity> remove announcement <CourseAnnouncementIDEntity>
49223
+ # gam course <CourseID> remove announcement <CourseAnnouncementID>
49178
49224
  # gam courses <CourseEntity> remove topic <CourseTopicIDEntity>
49179
49225
  # gam course <CourseID> remove topic <CourseTopicID>
49180
49226
  # gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity>
49181
49227
  # gam course <CourseID> remove teacher|student [owneracccess] <EmailAddress>
49182
49228
  def doCourseRemoveItems(courseIdList, getEntityListArg):
49183
49229
  croom = buildGAPIObject(API.CLASSROOM)
49184
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49230
+ removeType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49185
49231
  if not getEntityListArg:
49186
- if role in {Ent.STUDENT, Ent.TEACHER}:
49232
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49187
49233
  useOwnerAccess = GC.Values[GC.USE_COURSE_OWNER_ACCESS]
49188
49234
  if checkArgumentPresent(OWNER_ACCESS_OPTIONS):
49189
49235
  useOwnerAccess = True
49190
49236
  removeItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49191
- elif role == Ent.COURSE_ALIAS:
49237
+ elif removeType == Ent.COURSE_ALIAS:
49192
49238
  useOwnerAccess = False
49193
49239
  removeItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49194
- else: # role == Ent.COURSE_TOPIC:
49240
+ elif removeType == Ent.COURSE_TOPIC:
49195
49241
  useOwnerAccess = True
49196
49242
  removeItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49243
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49244
+ useOwnerAccess = True
49245
+ removeItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49197
49246
  courseParticipantLists = None
49198
49247
  else:
49199
- if role in {Ent.STUDENT, Ent.TEACHER}:
49248
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49200
49249
  useOwnerAccess = checkArgumentPresent(OWNER_ACCESS_OPTIONS)
49201
49250
  _, removeItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49202
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]})
49203
- elif role == Ent.COURSE_ALIAS:
49251
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[removeType]})
49252
+ elif removeType == Ent.COURSE_ALIAS:
49204
49253
  useOwnerAccess = False
49205
49254
  removeItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49206
- else: # role == Ent.COURSE_TOPIC:
49255
+ elif removeType == Ent.COURSE_TOPIC:
49207
49256
  useOwnerAccess = True
49208
49257
  removeItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49258
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49259
+ useOwnerAccess = True
49260
+ removeItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49209
49261
  courseParticipantLists = removeItems if isinstance(removeItems, dict) else None
49210
49262
  checkForExtraneousArguments()
49211
49263
  i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
@@ -49215,7 +49267,72 @@ def doCourseRemoveItems(courseIdList, getEntityListArg):
49215
49267
  if courseParticipantLists:
49216
49268
  removeItems = courseParticipantLists[courseId]
49217
49269
  courseId = addCourseIdScope(courseId)
49218
- _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, role)
49270
+ _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, removeType)
49271
+
49272
+ # gam courses <CourseEntity> update announcement <CourseAnnouncemntIDEntity>
49273
+ # [text <String>] [scheduledtime <Time>] [state published]
49274
+ # gam course <CourseID> update announcement <CourseAnnouncementID>
49275
+ # [text <String>] [scheduledtime <Time>] [state published]
49276
+ # gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
49277
+ # gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
49278
+ def doCourseUpdateItems(courseIdList, getEntityListArg):
49279
+ croom = buildGAPIObject(API.CLASSROOM)
49280
+ updateType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49281
+ if not getEntityListArg:
49282
+ if updateType == Ent.COURSE_TOPIC:
49283
+ useOwnerAccess = True
49284
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49285
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49286
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49287
+ useOwnerAccess = True
49288
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49289
+ body = getCourseAnnouncement(False)
49290
+ courseItemLists = None
49291
+ else:
49292
+ if updateType == Ent.COURSE_TOPIC:
49293
+ useOwnerAccess = True
49294
+ updateItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49295
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49296
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49297
+ useOwnerAccess = True
49298
+ updateItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49299
+ body = getCourseAnnouncement(False)
49300
+ courseItemLists = updateItems if isinstance(updateItems, dict) else None
49301
+ checkForExtraneousArguments()
49302
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
49303
+ addCIIdScope=courseItemLists is None)
49304
+ for courseId, courseInfo in coursesInfo.items():
49305
+ i += 1
49306
+ if courseItemLists:
49307
+ updateItems = courseItemLists[courseId]
49308
+ courseId = addCourseIdScope(courseId)
49309
+ jcount = len(updateItems)
49310
+ noScopeCourseId = removeCourseIdScope(courseId)
49311
+ if updateType == Ent.COURSE_TOPIC:
49312
+ service = courseInfo['croom'].courses().topics()
49313
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49314
+ service = courseInfo['croom'].courses().announcements()
49315
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, updateType, i, count)
49316
+ Ind.Increment()
49317
+ j = 0
49318
+ for updateItem in updateItems:
49319
+ j += 1
49320
+ try:
49321
+ callGAPI(service, 'patch',
49322
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49323
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49324
+ retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE],
49325
+ courseId=addCourseIdScope(courseId), id=updateItem, updateMask=','.join(body.keys()), body=body, fields='')
49326
+ entityActionPerformed([Ent.COURSE, courseId, updateType, updateItem], j, jcount)
49327
+ except GAPI.notFound:
49328
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.DOES_NOT_EXIST, j, jcount)
49329
+ except GAPI.forbidden:
49330
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.FORBIDDEN, j, jcount)
49331
+ except GAPI.permissionDenied:
49332
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.PERMISSION_DENIED, j, jcount)
49333
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49334
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], str(e), j, jcount)
49335
+ Ind.Decrement()
49219
49336
 
49220
49337
  # gam courses <CourseEntity> clear teachers|students
49221
49338
  # gam course <CourseID> clear teacher|student
@@ -77165,6 +77282,7 @@ COURSE_SUBCOMMANDS = {
77165
77282
  'add': (Act.ADD, doCourseAddItems),
77166
77283
  'clear': (Act.REMOVE, doCourseClearParticipants),
77167
77284
  'remove': (Act.REMOVE, doCourseRemoveItems),
77285
+ 'update': (Act.UPDATE, doCourseUpdateItems),
77168
77286
  'sync': (Act.SYNC, doCourseSyncParticipants),
77169
77287
  }
77170
77288
 
@@ -875,8 +875,11 @@ class GamCLArgs():
875
875
  OB_CONTACT_GROUP_ITEM = 'ContactGroupItem'
876
876
  OB_COURSE_ALIAS = 'CourseAlias'
877
877
  OB_COURSE_ALIAS_ENTITY = 'CourseAliasEntity'
878
+ OB_COURSE_ANNOUNCEMENT_ID = "CourseAnnouncementID"
878
879
  OB_COURSE_ANNOUNCEMENT_ID_ENTITY = "CourseAnnouncementIDEntity"
879
880
  OB_COURSE_ANNOUNCEMENT_STATE_LIST = "CourseAnnouncementStateList"
881
+ OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST = "CourseAnnouncementAddStateList"
882
+ OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST = "CourseAnnouncementUpdateStateList"
880
883
  OB_COURSE_ENTITY = 'CourseEntity'
881
884
  OB_COURSE_ID = 'CourseID'
882
885
  OB_COURSE_MATERIAL_ID_ENTITY = 'CourseMaterialIDEntity'
@@ -8,6 +8,7 @@
8
8
  - [Create and update courses](#create-and-update-courses)
9
9
  - [Delete courses](#delete-courses)
10
10
  - [Manage course aliases](#manage-course-aliases)
11
+ - [Manage course announcement](#manage-course-announcement)
11
12
  - [Manage course topics](#manage-course-topics)
12
13
  - [Display courses](#display-courses)
13
14
  - [Display course counts](#display-course-counts)
@@ -389,17 +390,40 @@ These commands can process multiple courses.
389
390
  gam courses <CourseEntity> add alias <CourseAliasEntity>
390
391
  gam courses <CourseEntity> delete alias <CourseAliasEntity>
391
392
  ```
393
+
394
+ ## Manage course announcements
395
+ These commands can process a single course.
396
+ ```
397
+ gam course <CourseID> add announcement
398
+ text <String> [scheduledtime <Time>] [state draft|published]
399
+ gam course <CourseID> delete announcement <CourseAnnouncementID>
400
+ gam course <CourseID> update announcement <CourseAnnouncementID>
401
+ [text <String>] [scheduledtime <Time>] [state published]
402
+ ```
403
+ These commands can process multiple courses.
404
+ ```
405
+ gam courses <CourseEntity> add announcement
406
+ text <String> [scheduledtime <Time>] [state draft|published]
407
+ gam courses <CourseEntity> delete announcement <CourseAnnouncementIDEntity>
408
+ gam courses <CourseEntity> update announcement <CourseAnnouncementIDEntity>
409
+ [text <String>] [scheduledtime <Time>] [state published]
410
+ ```
411
+
392
412
  ## Manage course topics
393
413
  These commands can process a single course.
394
414
  ```
395
415
  gam course <CourseID> add topic <CourseTopic>
396
416
  gam course <CourseID> delete topic <CourseTopicID>
417
+ gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
418
+
397
419
  ```
398
420
  These commands can process multiple courses.
399
421
  ```
400
422
  gam courses <CourseEntity> add topic <CourseTopicEntity>
401
423
  gam courses <CourseEntity> delete topic <CourseTopicIDEntity>
424
+ gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
402
425
  ```
426
+
403
427
  ## Display courses
404
428
  ```
405
429
  gam info course <CourseID> [owneremail] [alias|aliases] [show all|students|teachers] [countsonly]
@@ -10,6 +10,20 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
10
10
 
11
11
  See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
12
12
 
13
+ ### 7.11.00
14
+
15
+ Added commands to manage classroom/course announcements.
16
+
17
+ * See: https://github.com/GAM-team/GAM/wiki/Classroom-Courses#manage-course-announcements
18
+
19
+ Upgraded to OpenSSL 3.5.1.
20
+
21
+ ### 7.10.10
22
+
23
+ Added choices `text` and `hyperlink` to option `showwebviewlink` in `gam [<UserTypeEntity>] print|show shareddrives`.
24
+ * `showwebviewlink text` - Displays `https://drive.google.com/drive/folders/<SharedDriveID>`
25
+ * `showwebviewlink hyperlink` - Displays `=HYPERLINK("https://drive.google.com/drive/folders/<SharedDriveID>", "<SharedDriveName>")`
26
+
13
27
  ### 7.10.09
14
28
 
15
29
  Added option `showwebviewlink` to `gam [<UserTypeEntity>] print|show shareddrives` that
@@ -251,7 +251,7 @@ writes the credentials into the file oauth2.txt.
251
251
  admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
252
252
  admin@server:/Users/admin$ gam version
253
253
  WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
254
- GAM 7.10.09 - https://github.com/GAM-team/GAM - pyinstaller
254
+ GAM 7.11.00 - https://github.com/GAM-team/GAM - pyinstaller
255
255
  GAM Team <google-apps-manager@googlegroups.com>
256
256
  Python 3.13.5 64-bit final
257
257
  MacOS Sequoia 15.5 x86_64
@@ -989,7 +989,7 @@ writes the credentials into the file oauth2.txt.
989
989
  C:\>del C:\GAMConfig\oauth2.txt
990
990
  C:\>gam version
991
991
  WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
992
- GAM 7.10.09 - https://github.com/GAM-team/GAM - pythonsource
992
+ GAM 7.11.00 - https://github.com/GAM-team/GAM - pythonsource
993
993
  GAM Team <google-apps-manager@googlegroups.com>
994
994
  Python 3.13.5 64-bit final
995
995
  Windows-10-10.0.17134 AMD64
@@ -373,7 +373,7 @@ gam [<UserTypeEntity>] show shareddrives
373
373
  [adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
374
374
  [matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
375
375
  [fields <SharedDriveFieldNameList>]
376
- [showwebviewlink]
376
+ [showwebviewlink text|hyperlink]
377
377
  [formatjson]
378
378
  ```
379
379
  By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
@@ -381,6 +381,10 @@ By default, all Shared Drives are displayed; use the following options to select
381
381
  * `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
382
382
  * `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
383
383
 
384
+ Use option `showwebviewlink` to display the web view link for the Shared Drive.
385
+ * `showwebviewlink text` - Displays `https://drive.google.com/drive/folders/<SharedDriveID>`
386
+ * `showwebviewlink hyperlink` - Dsiplays `=HYPERLINK("https://drive.google.com/drive/folders/<SharedDriveID>", "<SharedDriveName>")`
387
+
384
388
  By default, Gam displays the information as an indented list of keys and values.
385
389
  * `formatjson` - Display the fields in JSON format.
386
390
  ```
@@ -388,7 +392,7 @@ gam [<UserTypeEntity>] print shareddrives [todrive <ToDriveAttribute>*]
388
392
  [adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
389
393
  [matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
390
394
  [fields <SharedDriveFieldNameList>]
391
- [showwebviewlink]
395
+ [showwebviewlink text|hyperlink]
392
396
  [formatjson [quotechar <Character>]]
393
397
  ```
394
398
  By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
@@ -396,6 +400,10 @@ By default, all Shared Drives are displayed; use the following options to select
396
400
  * `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
397
401
  * `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
398
402
 
403
+ Use option `showwebviewlink` to display the web view link for the Shared Drive.
404
+ * `showwebviewlink text` - Displays `https://drive.google.com/drive/folders/<SharedDriveID>`
405
+ * `showwebviewlink hyperlink` - Dsiplays `=HYPERLINK("https://drive.google.com/drive/folders/<SharedDriveID>", "<SharedDriveName>")`
406
+
399
407
  By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
400
408
  * `formatjson` - Display the fields in JSON format.
401
409