Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@
['name' => 'Api#getDownloadLimit', 'url' => '/api/v{version}/{token}/limit', 'verb' => 'GET', 'requirements' => $requirements],
['name' => 'Api#setDownloadLimit', 'url' => '/api/v{version}/{token}/limit', 'verb' => 'PUT', 'requirements' => $requirements],
['name' => 'Api#removeDownloadLimit', 'url' => '/api/v{version}/{token}/limit', 'verb' => 'DELETE', 'requirements' => $requirements],

['name' => 'Admin#setDefaultLimit', 'url' => '/api/v{version}/limit', 'verb' => 'PUT', 'requirements' => $requirements],
]
];
8 changes: 8 additions & 0 deletions lib/Controller/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace OCA\Files_DownloadLimit\Controller;

use OCA\Files_DownloadLimit\AppInfo\Application;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCSController;
Expand All @@ -24,6 +25,7 @@ public function __construct(
parent::__construct(Application::APP_ID, $request);
}

#[ApiRoute(verb: 'PUT', url: '/api/v{version}/limit', requirements: ['version' => 1])]
public function setDefaultLimit(int $limit): DataResponse {
if ($limit < 1) {
throw new OCSBadRequestException('Minimum limit is 1');
Expand All @@ -32,4 +34,10 @@ public function setDefaultLimit(int $limit): DataResponse {
$this->appConfig->setValueInt(Application::APP_ID, 'default-download-limit', $limit);
return new DataResponse();
}

#[ApiRoute(verb: 'DELETE', url: '/api/v{version}/limit', requirements: ['version' => 1])]
public function removeDefaultLimit(): DataResponse {
$this->appConfig->deleteKey(Application::APP_ID, 'default-download-limit');
return new DataResponse();
}
}
33 changes: 28 additions & 5 deletions src/services/AdminService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,39 @@
*/

import axios from '@nextcloud/axios'
import { showError } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
import { generateOcsUrl } from '@nextcloud/router'
import { logger } from '../logger.ts'

/**
* Set the default download limit for all shares.
*
* @param limit - The new default limit
*/
export async function setDefaultLimit(limit: number): Promise<[]> {
const response = await axios.put(generateOcsUrl('/apps/files_downloadlimit/api/v1/limit'), {
limit,
})
return response.data.ocs.data
export async function setDefaultLimit(limit: number): Promise<boolean> {
try {
await axios.put(generateOcsUrl('/apps/files_downloadlimit/api/v1/limit'), {
limit,
})
return true
} catch (error) {
logger.error('Failed to set default download limit', { error })
showError(t('files_downloadlimit', 'Failed to set default download limit'))
}
return false
}

/**
* Unset the configured default limit
*/
export async function removeDefaultLimit(): Promise<boolean> {
try {
await axios.delete(generateOcsUrl('/apps/files_downloadlimit/api/v1/limit'))
return true
} catch (error) {
logger.error('Failed to remove default download limit', { error })
showError(t('files_downloadlimit', 'Failed to remove default download limit'))
}
return false
}
109 changes: 56 additions & 53 deletions src/views/AdminSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,105 +7,108 @@
<NcSettingsSection
:name="t('files_downloadlimit', 'Download limit')"
:description="t('files_downloadlimit', 'Configure the default download limit for external shares.')">
<form @submit.prevent="handleSave">
<NcTextField
class="settings__field"
:label="t('files_downloadlimit', 'Set default download limit')"
type="number"
min="1"
:value="limit"
:helper-text="helperText"
:error="Boolean(helperText)"
:success="showSuccess"
:show-trailing-button="showTrailingButton"
trailing-button-icon="arrowRight"
@update:value="handleUpdateLimit"
@trailing-button-click="handleSave" />
</form>
<NcCheckboxRadioSwitch
v-model="enableDefaultLimit"
:loading="showLoading"
type="switch">
{{ t('files_downloadlimit', 'Default download limit for external shares') }}
</NcCheckboxRadioSwitch>

<NcTextField
v-show="enableDefaultLimit"
v-model="limit"
class="settings__field"
:disabled="!enableDefaultLimit || showLoading"
:label="t('files_downloadlimit', 'Set default download limit')"
type="number"
min="1"
:helper-text="helperText"
:error="Boolean(helperText)"
:success="showSuccess" />
<div v-show="!enableDefaultLimit" class="settings__placeholder" />
</NcSettingsSection>
</template>

<script lang="ts">
import { showError } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { translate as t } from '@nextcloud/l10n'
import { defineComponent } from 'vue'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import { logger } from '../logger.ts'
import { setDefaultLimit } from '../services/AdminService.ts'
import { removeDefaultLimit, setDefaultLimit } from '../services/AdminService.ts'

const defaultDownloadLimit = loadState<number>('files_downloadlimit', 'default-download-limit', -1)
// If a default is not set (-1) then the input should be empty
const limit: '' | number = defaultDownloadLimit === -1 ? '' : defaultDownloadLimit

export default defineComponent({
name: 'AdminSettings',

components: {
NcCheckboxRadioSwitch,
NcSettingsSection,
NcTextField,
},

data() {
return {
limit,
savedLimit: limit,
limit: Math.max(defaultDownloadLimit, 1),
enableDefaultLimit: defaultDownloadLimit !== -1,
showLoading: false,
showSuccess: false,
}
},

computed: {
helperText() {
if (typeof this.limit !== 'number') {
return ''
}
if (this.limit > 0) {
return ''
if (typeof this.limit === 'number' && this.limit <= 0) {
return t('files_downloadlimit', 'The minimum limit is 1')
}
return t('files_downloadlimit', 'The minimum limit is 1')
},

showTrailingButton() {
return typeof this.limit === 'number'
&& this.limit > 0
&& this.limit !== this.savedLimit
return ''
},
},

methods: {
t,

handleUpdateLimit(limit: string) {
this.limit = Number(limit) // emitted <input> value is string so we parse it to number
watch: {
async limit(limit: number) {
if (await setDefaultLimit(limit)) {
this.showSuccess = true
window.setTimeout(() => {
this.showSuccess = false
}, 1000)
}
},

async handleSave() {
const isValid = typeof this.limit === 'number' && this.limit > 0
if (!isValid) {
return
async enableDefaultLimit(enabled: boolean, oldValue: boolean) {
this.showLoading = true
let success: boolean
if (enabled) {
success = await setDefaultLimit(1)
} else {
success = await removeDefaultLimit()
}

try {
await setDefaultLimit(this.limit)
this.savedLimit = this.limit
this.showSuccess = true
setTimeout(() => {
this.showSuccess = false
}, 3000)
} catch (error) {
logger.error('Failed to set default download limit', { error })
showError(t('files_downloadlimit', 'Failed to set default download limit'))
if (!success) {
this.enableDefaultLimit = oldValue
}
this.showLoading = false
},
},

methods: {
t,
},
})
</script>

<style lang="scss" scoped>
.settings {
&__field {
max-width: 500px;
margin-top: calc(3 * var(--default-grid-baseline)) !important;
}

&__placeholder {
// ensure content does not jump when enable / disable the input
height: calc(var(--default-clickable-area) + 3 * var(--default-grid-baseline));
}
}
</style>
Loading