You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AdminSettingsSharingForm.vue 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <!--
  2. - @copyright 2023 Ferdinand Thiessen <opensource@fthiessen.de>
  3. -
  4. - @author Ferdinand Thiessen <opensource@fthiessen.de>
  5. -
  6. - @license AGPL-3.0-or-later
  7. -
  8. - This program is free software: you can redistribute it and/or modify
  9. - it under the terms of the GNU Affero General Public License as
  10. - published by the Free Software Foundation, either version 3 of the
  11. - License, or (at your option) any later version.
  12. -
  13. - This program is distributed in the hope that it will be useful,
  14. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. - GNU Affero General Public License for more details.
  17. -
  18. - You should have received a copy of the GNU Affero General Public License
  19. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. -
  21. -->
  22. <template>
  23. <form class="sharing">
  24. <NcCheckboxRadioSwitch aria-controls="settings-sharing-api settings-sharing-api-settings settings-sharing-default-permissions settings-sharing-privary-related"
  25. type="switch"
  26. :checked.sync="settings.enabled">
  27. {{ t('settings', 'Allow apps to use the Share API') }}
  28. </NcCheckboxRadioSwitch>
  29. <div v-show="settings.enabled" id="settings-sharing-api-settings" class="sharing__sub-section">
  30. <NcCheckboxRadioSwitch :checked.sync="settings.allowResharing">
  31. {{ t('settings', 'Allow resharing') }}
  32. </NcCheckboxRadioSwitch>
  33. <NcCheckboxRadioSwitch :checked.sync="settings.allowGroupSharing">
  34. {{ t('settings', 'Allow sharing with groups') }}
  35. </NcCheckboxRadioSwitch>
  36. <NcCheckboxRadioSwitch :checked.sync="settings.onlyShareWithGroupMembers">
  37. {{ t('settings', 'Restrict users to only share with users in their groups') }}
  38. </NcCheckboxRadioSwitch>
  39. <div v-show="settings.onlyShareWithGroupMembers" id="settings-sharing-api-excluded-groups" class="sharing__labeled-entry sharing__input">
  40. <label for="settings-sharing-only-group-members-excluded-groups">{{ t('settings', 'Ignore the following groups when checking group membership') }}</label>
  41. <NcSettingsSelectGroup id="settings-sharing-only-group-members-excluded-groups"
  42. v-model="settings.onlyShareWithGroupMembersExcludeGroupList"
  43. :label="t('settings', 'Ignore the following groups when checking group membership')"
  44. style="width: 100%" />
  45. </div>
  46. </div>
  47. <div v-show="settings.enabled" id="settings-sharing-api" class="sharing__section">
  48. <NcCheckboxRadioSwitch type="switch"
  49. aria-controls="settings-sharing-api-public-link"
  50. :checked.sync="settings.allowLinks">
  51. {{ t('settings', 'Allow users to share via link and emails') }}
  52. </NcCheckboxRadioSwitch>
  53. <fieldset v-show="settings.allowLinks" id="settings-sharing-api-public-link" class="sharing__sub-section">
  54. <NcCheckboxRadioSwitch :checked.sync="settings.allowPublicUpload">
  55. {{ t('settings', 'Allow public uploads') }}
  56. </NcCheckboxRadioSwitch>
  57. <NcCheckboxRadioSwitch :checked.sync="settings.enableLinkPasswordByDefault">
  58. {{ t('settings', 'Always ask for a password') }}
  59. </NcCheckboxRadioSwitch>
  60. <NcCheckboxRadioSwitch :checked.sync="settings.enforceLinksPassword" :disabled="!settings.enableLinkPasswordByDefault">
  61. {{ t('settings', 'Enforce password protection') }}
  62. </NcCheckboxRadioSwitch>
  63. <label v-if="settings.passwordExcludedGroupsFeatureEnabled" class="sharing__labeled-entry sharing__input">
  64. <span>{{ t('settings', 'Exclude groups from password requirements') }}</span>
  65. <NcSettingsSelectGroup v-model="settings.passwordExcludedGroups"
  66. style="width: 100%"
  67. :disabled="!settings.enforceLinksPassword || !settings.enableLinkPasswordByDefault" />
  68. </label>
  69. <label class="sharing__labeled-entry sharing__input">
  70. <span>{{ t('settings', 'Exclude groups from creating link shares') }}</span>
  71. <NcSettingsSelectGroup v-model="settings.allowLinksExcludeGroups"
  72. :label="t('settings', 'Exclude groups from creating link shares')"
  73. style="width: 100%" />
  74. </label>
  75. </fieldset>
  76. <label>{{ t('settings', 'Limit sharing based on groups') }}</label>
  77. <div class="sharing__sub-section">
  78. <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
  79. name="excludeGroups" value="no"
  80. type="radio" @update:checked="onUpdateExcludeGroups">
  81. {{ t('settings', 'Allow sharing for everyone (default)') }}
  82. </NcCheckboxRadioSwitch>
  83. <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
  84. name="excludeGroups" value="yes"
  85. type="radio" @update:checked="onUpdateExcludeGroups">
  86. {{ t('settings', 'Exclude some groups from sharing') }}
  87. </NcCheckboxRadioSwitch>
  88. <NcCheckboxRadioSwitch :checked.sync="settings.excludeGroups"
  89. name="excludeGroups" value="allow"
  90. type="radio" @update:checked="onUpdateExcludeGroups">
  91. {{ t('settings', 'Limit sharing to some groups') }}
  92. </NcCheckboxRadioSwitch>
  93. <div v-show="settings.excludeGroups !== 'no'" class="sharing__labeled-entry sharing__input">
  94. <NcSettingsSelectGroup id="settings-sharing-excluded-groups"
  95. v-model="settings.excludeGroupsList"
  96. aria-describedby="settings-sharing-excluded-groups-desc"
  97. :label="settings.excludeGroups === 'allow' ? t('settings', 'Groups allowed to share') : t('settings', 'Groups excluded from sharing')"
  98. :disabled="settings.excludeGroups === 'no'"
  99. style="width: 100%" />
  100. <em id="settings-sharing-excluded-groups-desc">{{ t('settings', 'Not allowed groups will still be able to receive shares, but not to initiate them.') }}</em>
  101. </div>
  102. </div>
  103. <NcCheckboxRadioSwitch type="switch"
  104. aria-controls="settings-sharing-api-expiration"
  105. :checked.sync="settings.defaultInternalExpireDate">
  106. {{ t('settings', 'Set default expiration date for shares') }}
  107. </NcCheckboxRadioSwitch>
  108. <fieldset v-show="settings.defaultInternalExpireDate" id="settings-sharing-api-expiration" class="sharing__sub-section">
  109. <NcCheckboxRadioSwitch :checked.sync="settings.enforceInternalExpireDate">
  110. {{ t('settings', 'Enforce expiration date') }}
  111. </NcCheckboxRadioSwitch>
  112. <NcTextField type="number"
  113. class="sharing__input"
  114. :label="t('settings', 'Default expiration time of new shares in days')"
  115. :placeholder="t('settings', 'Expire shares after x days')"
  116. :value.sync="settings.internalExpireAfterNDays" />
  117. </fieldset>
  118. <NcCheckboxRadioSwitch type="switch"
  119. aria-controls="settings-sharing-remote-api-expiration"
  120. :checked.sync="settings.defaultRemoteExpireDate">
  121. {{ t('settings', 'Set default expiration date for shares to other servers') }}
  122. </NcCheckboxRadioSwitch>
  123. <fieldset v-show="settings.defaultRemoteExpireDate" id="settings-sharing-remote-api-expiration" class="sharing__sub-section">
  124. <NcCheckboxRadioSwitch :checked.sync="settings.enforceRemoteExpireDate">
  125. {{ t('settings', 'Enforce expiration date for remote shares') }}
  126. </NcCheckboxRadioSwitch>
  127. <NcTextField type="number"
  128. class="sharing__input"
  129. :label="t('settings', 'Default expiration time of remote shares in days')"
  130. :placeholder="t('settings', 'Expire remote shares after x days')"
  131. :value.sync="settings.remoteExpireAfterNDays" />
  132. </fieldset>
  133. <NcCheckboxRadioSwitch type="switch"
  134. aria-controls="settings-sharing-api-api-expiration"
  135. :checked.sync="settings.defaultExpireDate"
  136. :disabled="!settings.allowLinks">
  137. {{ t('settings', 'Set default expiration date for shares via link or mail') }}
  138. </NcCheckboxRadioSwitch>
  139. <fieldset v-show="settings.allowLinks && settings.defaultExpireDate" id="settings-sharing-api-api-expiration" class="sharing__sub-section">
  140. <NcCheckboxRadioSwitch :checked.sync="settings.enforceExpireDate">
  141. {{ t('settings', 'Enforce expiration date for remote shares') }}
  142. </NcCheckboxRadioSwitch>
  143. <NcTextField type="number"
  144. class="sharing__input"
  145. :label="t('settings', 'Default expiration time of shares in days')"
  146. :placeholder="t('settings', 'Expire shares after x days')"
  147. :value.sync="settings.expireAfterNDays" />
  148. </fieldset>
  149. </div>
  150. <div v-show="settings.enabled" id="settings-sharing-privary-related" class="sharing__section">
  151. <h3>{{ t('settings', 'Privacy settings for sharing') }}</h3>
  152. <NcCheckboxRadioSwitch type="switch"
  153. aria-controls="settings-sharing-privacy-user-enumeration"
  154. :checked.sync="settings.allowShareDialogUserEnumeration">
  155. {{ t('settings', 'Allow username autocompletion in share dialog and allow access to the system address book') }}
  156. </NcCheckboxRadioSwitch>
  157. <fieldset v-show="settings.allowShareDialogUserEnumeration" id="settings-sharing-privacy-user-enumeration" class="sharing__sub-section">
  158. <em>
  159. {{ t('settings', 'If autocompletion "same group" and "phone number integration" are enabled a match in either is enough to show the user.') }}
  160. </em>
  161. <NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToGroup">
  162. {{ t('settings', 'Allow username autocompletion to users within the same groups and limit system address books to users in the same groups') }}
  163. </NcCheckboxRadioSwitch>
  164. <NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToPhone">
  165. {{ t('settings', 'Allow username autocompletion to users based on phone number integration') }}
  166. </NcCheckboxRadioSwitch>
  167. </fieldset>
  168. <NcCheckboxRadioSwitch type="switch" :checked.sync="settings.restrictUserEnumerationFullMatch">
  169. {{ t('settings', 'Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)') }}
  170. </NcCheckboxRadioSwitch>
  171. <NcCheckboxRadioSwitch type="switch" :checked.sync="publicShareDisclaimerEnabled">
  172. {{ t('settings', 'Show disclaimer text on the public link upload page (only shown when the file list is hidden)') }}
  173. </NcCheckboxRadioSwitch>
  174. <div v-if="typeof settings.publicShareDisclaimerText === 'string'"
  175. aria-describedby="settings-sharing-privary-related-disclaimer-hint"
  176. class="sharing__sub-section">
  177. <NcTextArea class="sharing__input"
  178. :label="t('settings', 'Disclaimer text')"
  179. aria-describedby="settings-sharing-privary-related-disclaimer-hint"
  180. :value="settings.publicShareDisclaimerText"
  181. @update:value="onUpdateDisclaimer" />
  182. <em id="settings-sharing-privary-related-disclaimer-hint" class="sharing__input">
  183. {{ t('settings', 'This text will be shown on the public link upload page when the file list is hidden.') }}
  184. </em>
  185. </div>
  186. </div>
  187. <div id="settings-sharing-default-permissions" class="sharing__section">
  188. <h3>{{ t('settings', 'Default share permissions') }}</h3>
  189. <SelectSharingPermissions :value.sync="settings.defaultPermissions" />
  190. </div>
  191. </form>
  192. </template>
  193. <script lang="ts">
  194. import {
  195. NcCheckboxRadioSwitch,
  196. NcSettingsSelectGroup,
  197. NcTextArea,
  198. NcTextField,
  199. } from '@nextcloud/vue'
  200. import { showError, showSuccess } from '@nextcloud/dialogs'
  201. import { translate as t } from '@nextcloud/l10n'
  202. import { loadState } from '@nextcloud/initial-state'
  203. import { defineComponent } from 'vue'
  204. import SelectSharingPermissions from './SelectSharingPermissions.vue'
  205. import { snakeCase, debounce } from 'lodash'
  206. interface IShareSettings {
  207. enabled: boolean
  208. allowGroupSharing: boolean
  209. allowLinks: boolean
  210. allowLinksExcludeGroups: unknown
  211. allowPublicUpload: boolean
  212. allowResharing: boolean
  213. allowShareDialogUserEnumeration: boolean
  214. restrictUserEnumerationToGroup: boolean
  215. restrictUserEnumerationToPhone: boolean
  216. restrictUserEnumerationFullMatch: boolean
  217. restrictUserEnumerationFullMatchUserId: boolean
  218. restrictUserEnumerationFullMatchEmail: boolean
  219. restrictUserEnumerationFullMatchIgnoreSecondDN: boolean
  220. enforceLinksPassword: boolean
  221. passwordExcludedGroups: string[]
  222. passwordExcludedGroupsFeatureEnabled: boolean
  223. onlyShareWithGroupMembers: boolean
  224. onlyShareWithGroupMembersExcludeGroupList: string[]
  225. defaultExpireDate: boolean
  226. expireAfterNDays: string
  227. enforceExpireDate: boolean
  228. excludeGroups: string
  229. excludeGroupsList: string[]
  230. publicShareDisclaimerText?: string
  231. enableLinkPasswordByDefault: boolean
  232. defaultPermissions: number
  233. defaultInternalExpireDate: boolean
  234. internalExpireAfterNDays: string
  235. enforceInternalExpireDate: boolean
  236. defaultRemoteExpireDate: boolean
  237. remoteExpireAfterNDays: string
  238. enforceRemoteExpireDate: boolean
  239. }
  240. export default defineComponent({
  241. name: 'AdminSettingsSharingForm',
  242. components: {
  243. NcCheckboxRadioSwitch,
  244. NcSettingsSelectGroup,
  245. NcTextArea,
  246. NcTextField,
  247. SelectSharingPermissions,
  248. },
  249. data() {
  250. return {
  251. settingsData: loadState<IShareSettings>('settings', 'sharingSettings'),
  252. }
  253. },
  254. computed: {
  255. settings() {
  256. console.warn('new proxy')
  257. return new Proxy(this.settingsData, {
  258. get(target, property) {
  259. return target[property]
  260. },
  261. set(target, property: string, newValue) {
  262. const configName = `shareapi_${snakeCase(property)}`
  263. const value = typeof newValue === 'boolean' ? (newValue ? 'yes' : 'no') : (typeof newValue === 'string' ? newValue : JSON.stringify(newValue))
  264. window.OCP.AppConfig.setValue('core', configName, value)
  265. target[property] = newValue
  266. return true
  267. },
  268. })
  269. },
  270. publicShareDisclaimerEnabled: {
  271. get() {
  272. return typeof this.settingsData.publicShareDisclaimerText === 'string'
  273. },
  274. set(value) {
  275. if (value) {
  276. this.settingsData.publicShareDisclaimerText = ''
  277. } else {
  278. this.onUpdateDisclaimer()
  279. }
  280. },
  281. },
  282. },
  283. methods: {
  284. t,
  285. onUpdateDisclaimer: debounce(function(value?: string) {
  286. const options = {
  287. success() {
  288. if (value) {
  289. showSuccess(t('settings', 'Changed disclaimer text'))
  290. } else {
  291. showSuccess(t('settings', 'Deleted disclaimer text'))
  292. }
  293. },
  294. error() {
  295. showError(t('settings', 'Could not set disclaimer text'))
  296. },
  297. }
  298. if (!value) {
  299. window.OCP.AppConfig.deleteKey('core', 'shareapi_public_link_disclaimertext', options)
  300. } else {
  301. window.OCP.AppConfig.setValue('core', 'shareapi_public_link_disclaimertext', value, options)
  302. }
  303. this.settingsData.publicShareDisclaimerText = value
  304. }, 500) as (v?: string) => void,
  305. onUpdateExcludeGroups: debounce(function(value: string) {
  306. window.OCP.AppConfig.setValue('core', 'excludeGroups', value)
  307. this.settings.excludeGroups = value
  308. }, 500) as (v?: string) => void
  309. },
  310. })
  311. </script>
  312. <style scoped lang="scss">
  313. .sharing {
  314. display: flex;
  315. flex-direction: column;
  316. gap: 12px;
  317. &__labeled-entry {
  318. display: flex;
  319. flex: 1 0;
  320. flex-direction: column;
  321. gap: 4px;
  322. }
  323. &__section {
  324. display: flex;
  325. flex-direction: column;
  326. gap: 4px;
  327. margin-block-end: 12px
  328. }
  329. &__sub-section {
  330. display: flex;
  331. flex-direction: column;
  332. gap: 4px;
  333. margin-inline-start: 44px;
  334. margin-block-end: 12px
  335. }
  336. &__input {
  337. max-width: 500px;
  338. // align with checkboxes
  339. margin-inline-start: 14px;
  340. :deep(.v-select.select) {
  341. width: 100%;
  342. }
  343. }
  344. }
  345. @media only screen and (max-width: 350px) {
  346. // ensure no overflow happens on small devices (required for WCAG)
  347. .sharing {
  348. &__sub-section {
  349. margin-inline-start: 14px;
  350. }
  351. }
  352. }
  353. </style>