Skip to content

feat: maintenance update, basic object storage support, minor cleanup #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 21, 2024
Merged
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,14 @@

All notable changes to this project will be documented in this file.

## [0.2.0 - 2024-10-20]

Maintenance update. Update NC versions to support NC30+ only.

### Added

- Added basic ObjectStorage support (/tmp folder used to execute binary scripts)

## [0.1.9 - 2023-12-14]

Maintenance update.
4 changes: 2 additions & 2 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ Nextcloud Python API Framework
- [MediaDC](https://apps.nextcloud.com/apps/mediadc) - Nextcloud Media Duplicate Collector app
]]>
</description>
<version>0.1.9</version>
<version>0.2.0</version>
<licence>agpl</licence>
<author mail="andrey18106x@gmail.com" homepage="https://github.com/andrey18106">Andrey Borysenko</author>
<author mail="bigcat88@icloud.com" homepage="https://github.com/bigcat88">Alexander Piskun</author>
@@ -37,7 +37,7 @@ Nextcloud Python API Framework
<repository type="git">https://github.com/cloud-py-api/cloud_py_api</repository>
<dependencies>
<php min-version="7.4" min-int-size="64" />
<nextcloud min-version="28" max-version="29" />
<nextcloud min-version="30" max-version="31" />
</dependencies>
<repair-steps>
<post-migration>
14 changes: 4 additions & 10 deletions lib/Command/GetFileContentsCommand.php
Original file line number Diff line number Diff line change
@@ -45,17 +45,11 @@ class GetFileContentsCommand extends Command {
public const ARGUMENT_FILE_ID = 'fileid';
public const ARGUMENT_USER_ID = 'userid';

/** @var IRootFolder */
private $rootFolder;

/** @var LoggerInterface */
private $logger;

public function __construct(IRootFolder $rootFolder, LoggerInterface $logger) {
public function __construct(
private readonly IRootFolder $rootFolder,
private readonly LoggerInterface $logger
) {
parent::__construct();

$this->rootFolder = $rootFolder;
$this->logger = $logger;
}

protected function configure(): void {
63 changes: 11 additions & 52 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@

namespace OCA\Cloud_Py_API\Controller;

use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
@@ -38,80 +40,37 @@
use OCA\Cloud_Py_API\Service\UtilsService;

class SettingsController extends Controller {
/** @var SettingsService */
private $service;

/** @var UtilsService */
private $utils;

public function __construct(
IRequest $request,
SettingsService $service,
UtilsService $utils
private readonly SettingsService $service,
private readonly UtilsService $utils
) {
parent::__construct(Application::APP_ID, $request);

$this->service = $service;
$this->utils = $utils;
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @return JSONResponse array of all settings
*/
public function index() {
#[NoAdminRequired]
public function index(): JSONResponse {
return new JSONResponse($this->service->getSettings(), Http::STATUS_OK);
}

/**
* @NoCSRFRequired
*
* @param array $settings
*
* @return JSONResponse
*/
public function update($settings) {
#[PasswordConfirmationRequired]
public function update(array $settings): JSONResponse {
return new JSONResponse($this->service->updateSettings($settings), Http::STATUS_OK);
}

/**
* @NoCSRFRequired
*
* @param array $setting
*
* @return JSONResponse
*/
public function updateSetting($setting) {
#[PasswordConfirmationRequired]
public function updateSetting(array $setting): JSONResponse {
return new JSONResponse($this->service->updateSetting($setting), Http::STATUS_OK);
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param int $id
*/
public function getSettingById($id): JSONResponse {
public function getSettingById(int $id): JSONResponse {
return new JSONResponse($this->service->getSettingById($id), Http::STATUS_OK);
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $name
*/
public function getSettingByName($name): JSONResponse {
return new JSONResponse($this->service->getSettingByName($name), Http::STATUS_OK);
}

/**
* @NoCSRFRequired
*
* @return JSONResponse array of system configuration
*/
public function systemInfo() {
return new JSONResponse($this->utils->getSystemInfo(), Http::STATUS_OK);
}
2 changes: 1 addition & 1 deletion lib/Db/SettingMapper.php
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ public function __construct(IDBConnection $db) {
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
*/
public function find(int $id): Entity {
public function find(int $id): Setting {
$qb = $this->db->getQueryBuilder();

$qb->select('*')
13 changes: 4 additions & 9 deletions lib/Migration/AppDataInitializationStep.php
Original file line number Diff line number Diff line change
@@ -37,15 +37,10 @@
use OCA\Cloud_Py_API\Service\UtilsService;

class AppDataInitializationStep implements IRepairStep {
/** @var SettingsMapper */
private $settingMapper;

/** @var UtilsService */
private $utils;

public function __construct(SettingMapper $settingMapper, UtilsService $utils) {
$this->settingMapper = $settingMapper;
$this->utils = $utils;
public function __construct(
private readonly SettingMapper $settingMapper,
private readonly UtilsService $utils,
) {
}

public function getName(): string {
8 changes: 3 additions & 5 deletions lib/Migration/AppUpdateStep.php
Original file line number Diff line number Diff line change
@@ -35,11 +35,9 @@
use OCA\Cloud_Py_API\Service\UtilsService;

class AppUpdateStep implements IRepairStep {
/** @var UtilsService */
private $utils;

public function __construct(UtilsService $utils) {
$this->utils = $utils;
public function __construct(
private readonly UtilsService $utils,
) {
}

public function getName(): string {
45 changes: 26 additions & 19 deletions lib/Service/PythonService.php
Original file line number Diff line number Diff line change
@@ -32,34 +32,29 @@

use OCA\Cloud_Py_API\Db\SettingMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\ITempManager;

class PythonService {
/** @var string */
private $pythonCommand;

/** @var string */
private $ncInstanceId;

/** @var string */
private $ncDataFolder;

/** @var UtilsService */
private $utils;
private string $pythonCommand;
private string $ncInstanceId;
private string $ncDataFolder;
private bool $isObjectStorage;

public function __construct(
IConfig $config,
private readonly IConfig $config,
SettingMapper $settingMapper,
UtilsService $utils
private readonly UtilsService $utils,
private readonly ITempManager $tempManager,
) {
try {
$pythonCommand = $settingMapper->findByName('python_command');
$this->pythonCommand = $pythonCommand->getValue();
} catch (DoesNotExistException $e) {
$this->pythonCommand = '/usr/bin/python3';
}
$this->utils = $utils;
$this->ncInstanceId = $config->getSystemValue('instanceid');
$this->ncDataFolder = $config->getSystemValue('datadirectory');
$this->ncInstanceId = $this->config->getSystemValue('instanceid');
$this->ncDataFolder = $this->config->getSystemValue('datadirectory');
$this->isObjectStorage = $this->config->getSystemValue('objectstore', null) !== null;
}

/**
@@ -84,10 +79,14 @@ public function run(
array $scriptParams = [],
bool $nonBlocking = false,
array $env = [],
bool $binary = false
bool $binary = false,
) {
if ($binary) {
$cwd = $this->ncDataFolder . '/appdata_' . $this->ncInstanceId . '/' . $appId . '/';
if ($this->isObjectStorage) {
$cwd = ''; // scriptName should already include absolute path (/tmp/...)
} else {
$cwd = $this->ncDataFolder . '/appdata_' . $this->ncInstanceId . '/' . $appId . '/';
}
} else {
$cwd = $this->utils->getCustomAppsDirectory() . $appId . '/';
}
@@ -111,7 +110,15 @@ public function run(
}
if ($nonBlocking) {
if ($binary) {
$logFile = $cwd . 'logs/' . date('d-m-Y_H-i-s', time()) . '.log';
if (!$this->isObjectStorage) {
$logFile = $cwd . 'logs/' . date('d-m-Y_H-i-s', time()) . '.log';
} else {
$tempLogsDir = $this->tempManager->getTempBaseDir() . '/' . $appId . '/logs/';
if (!file_exists($tempLogsDir)) {
mkdir($tempLogsDir, 0700, true);
}
$logFile = $tempLogsDir . $appId . '_' . date('d-m-Y_H-i-s', time()) . '.log';
}
} else {
$appDataDir = $this->ncDataFolder . '/appdata_' . $this->ncInstanceId . '/' . $appId . '/';
$pyBitecodeEnvVar = 'PYTHONBYTECODEBASE="' . $appDataDir . '" ';
8 changes: 3 additions & 5 deletions lib/Service/SettingsService.php
Original file line number Diff line number Diff line change
@@ -35,11 +35,9 @@
use OCA\Cloud_Py_API\Db\SettingMapper;

class SettingsService {
/** @var SettingMapper */
private $mapper;

public function __construct(SettingMapper $settingMapper) {
$this->mapper = $settingMapper;
public function __construct(
private readonly SettingMapper $mapper
) {
}

/**
162 changes: 132 additions & 30 deletions lib/Service/UtilsService.php
Original file line number Diff line number Diff line change
@@ -30,9 +30,15 @@

use bantu\IniGetWrapper\IniGetWrapper;
use OC\Archive\TAR;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\App\IAppManager;

use OCP\ITempManager;
use Psr\Log\LoggerInterface;

use OCA\ServerInfo\DatabaseStatistics;
@@ -42,33 +48,15 @@
use OCA\Cloud_Py_API\Db\SettingMapper;

class UtilsService {
/** @var IConfig */
private $config;

/** @var SettingMapper */
private $settingMapper;

/** @var IAppManager */
private $appManager;

/** @var DatabaseStatistics */
private $databaseStatistics;

/** @var LoggerInterface */
private $logger;

public function __construct(
IConfig $config,
SettingMapper $settingMapper,
IAppManager $appManager,
?DatabaseStatistics $databaseStatistics,
LoggerInterface $logger
private readonly IConfig $config,
private readonly SettingMapper $settingMapper,
private readonly IAppManager $appManager,
private readonly ?DatabaseStatistics $databaseStatistics,
private readonly LoggerInterface $logger,
private readonly IAppDataFactory $appDataFactory,
private readonly ITempManager $tempManager,
) {
$this->config = $config;
$this->settingMapper = $settingMapper;
$this->appManager = $appManager;
$this->databaseStatistics = $databaseStatistics;
$this->logger = $logger;
}

public function getNCLogLevel(): string {
@@ -361,14 +349,58 @@ public function downloadPythonBinaryDir(
string $filename = 'main',
bool $update = false
): array {
$isObjectStore = $this->config->getSystemValue('objectstore', null) !== null;
if (isset($binariesFolder['success']) && $binariesFolder['success']) {
$dir = $binariesFolder['path'] . '/';
} else {
return $binariesFolder; // Return getAppDataFolder result
if ($isObjectStore) {
$appDataFolder = $this->appDataFactory->get($appId)->getFolder($binariesFolder['folderName']);
/** @var ISimpleFolder|ISimpleFile $nodes */
$nodes = $appDataFolder->getDirectoryListing();
$binariesTempFolder = $this->tempManager->getTemporaryFolder($appId . $binariesFolder['folderName']);
$dir = $binariesTempFolder;
$binariesFolder['path'] = $dir;
$binaryArchiveName = $appId . '_' . $this->getBinaryName() . '.tar.gz';
foreach ($nodes as $node) {
if ($node instanceof ISimpleFile && $node->getName() === $binaryArchiveName) {
// Copy archive to temp folder
try {
$handle = $node->read();
if ($handle === false) {
return ['success' => false, 'error' => 'Failed to read python binary file'];
}

$binariesArchiveFile = fopen($dir . '/' . $binaryArchiveName, 'wb');
if ($binariesArchiveFile === false) {
return ['success' => false, 'error' => 'Failed to write python binary file'];
}
while (!feof($handle)) {
$chunk = fread($handle, 4 * 1024 * 1024);
if ($chunk === false) {
return ['success' => false, 'error' => 'Failed to read python binary file'];
}
fwrite($binariesArchiveFile, $chunk);
}
fclose($handle);
fclose($binariesArchiveFile);
$this->unTarGz($binariesFolder, $binaryArchiveName, true);
} catch (NotPermittedException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
$shouldDownloadBinary = !file_exists($dir . '/' . $binaryArchiveName);
} else {
return $binariesFolder; // Return getAppDataFolder result
}
}
$file_name = $filename . '.tar.gz';
$save_file_loc = $dir . $file_name;
$shouldDownloadBinary = $this->compareBinaryDirectoryHashes($url, $binariesFolder, $appId);
if (isset($shouldDownloadBinary) && !$shouldDownloadBinary) {
$shouldDownloadBinary = $this->compareBinaryDirectoryHashes($url, $binariesFolder, $appId);
} else {
$shouldDownloadBinary = true;
}

if (!file_exists($dir . $filename) || ($update && $shouldDownloadBinary)) {
$cURL = curl_init($url);
@@ -382,7 +414,21 @@ public function downloadPythonBinaryDir(
curl_exec($cURL);
curl_close($cURL);
fclose($fp);
$unpacked = $this->unTarGz($binariesFolder, $filename . '.tar.gz');
$unpacked = $this->unTarGz($binariesFolder, $filename . '.tar.gz', $isObjectStore);
if ($isObjectStore) {
// Save binaries archive to AppData (object storage)
$appDataFolder = $this->appDataFactory->get($appId)->getFolder($binariesFolder['folderName']);
$handle = fopen($save_file_loc, 'rb');
if ($handle === false) {
return ['success' => false, 'error' => 'Failed to read python binary file'];
}
try {
$appDataArchiveFile = $appDataFolder->newFile($binaryArchiveName);
$appDataArchiveFile->putContent($handle);
} catch (NotPermittedException|NotFoundException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
unlink($save_file_loc);
return [
'downloaded' => true,
@@ -397,6 +443,62 @@ public function downloadPythonBinaryDir(
];
}

public function prefetchAppDataFile(
string $appId,
string $folderName,
string $fileName,
) {
$appDataFolder = $this->appDataFactory->get($appId)->getFolder($folderName);
/** @var ISimpleFolder|ISimpleFile $nodes */
$nodes = $appDataFolder->getDirectoryListing();
// $tempFolder = $this->tempManager->getTemporaryFolder($appId . $folderName);
$tempFolder = $this->tempManager->getTempBaseDir() . '/' . $appId . '/' . $folderName;
if (!file_exists($tempFolder)) {
mkdir($tempFolder, 0700, true);
}
if (file_exists($tempFolder . '/' . $fileName)) {
return [
'success' => true,
'path' => $this->tempManager->getTempBaseDir() . '/' . $appId . '/',
];
}
foreach ($nodes as $node) {
if ($node instanceof ISimpleFile && $node->getName() === $fileName) {
// Copy archive to temp folder
try {
$handle = $node->read();
if ($handle === false) {
return ['success' => false, 'error' => 'Failed to read python binary file'];
}

$binariesArchiveFile = fopen($tempFolder . '/' . $fileName, 'wb');
if ($binariesArchiveFile === false) {
return ['success' => false, 'error' => 'Failed to write python binary file'];
}
while (!feof($handle)) {
$chunk = fread($handle, 4 * 1024 * 1024);
if ($chunk === false) {
return ['success' => false, 'error' => 'Failed to read python binary file'];
}
fwrite($binariesArchiveFile, $chunk);
}
fclose($handle);
fclose($binariesArchiveFile);
$this->unTarGz([
'success' => true,
'path' => $tempFolder,
], $fileName, true);
} catch (NotPermittedException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
return [
'success' => true,
'path' => $tempFolder,
];
}

/**
* Extract tar.gz file
*
@@ -405,8 +507,8 @@ public function downloadPythonBinaryDir(
*
* @return array
*/
public function unTarGz(array $binariesFolder, string $src_filename): array {
if (isset($binariesFolder['success']) && $binariesFolder['success']) {
public function unTarGz(array $binariesFolder, string $src_filename, bool $objectStore = false): array {
if (isset($binariesFolder['success']) && $binariesFolder['success'] || $objectStore) {
$dir = $binariesFolder['path'] . '/';
$src_file = $dir . $src_filename;
$archive = new TAR($src_file);
13 changes: 4 additions & 9 deletions lib/Settings/AdminSection.php
Original file line number Diff line number Diff line change
@@ -34,15 +34,10 @@
use OCP\Settings\IIconSection;

class AdminSection implements IIconSection {
/** @var IL10N */
private $l;

/** @var UrlGenerator */
private $urlGenerator;

public function __construct(IL10N $l, URLGenerator $urlGenerator) {
$this->l = $l;
$this->urlGenerator = $urlGenerator;
public function __construct(
private readonly IL10N $l,
private readonly URLGenerator $urlGenerator
) {
}

public function getId() {
15,445 changes: 7,574 additions & 7,871 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cloud_py_api",
"description": "Nextcloud Python API (Framework)",
"version": "0.1.9",
"version": "0.2.0",
"keywords": [
"nextcloud",
"python",
@@ -40,32 +40,33 @@
"extends @nextcloud/browserslist-config"
],
"dependencies": {
"@nextcloud/auth": "^2.2.1",
"@nextcloud/axios": "^2.4.0",
"@nextcloud/dialogs": "^5.0.3",
"@nextcloud/initial-state": "^2.1.0",
"@nextcloud/l10n": "^2.2.0",
"@nextcloud/moment": "^1.2.2",
"@nextcloud/router": "^2.2.0",
"@nextcloud/vue": "^8.3.0",
"@nextcloud/auth": "^2.4.0",
"@nextcloud/axios": "^2.5.1",
"@nextcloud/dialogs": "^5.3.7",
"@nextcloud/initial-state": "^2.2.0",
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/moment": "^1.3.1",
"@nextcloud/password-confirmation": "^5.1.1",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.19.0",
"vue": "^2.7.14",
"vuex": "^3.6.2",
"vue-material-design-icons": "^5.3.0",
"vue-router": "^3.5.3",
"vue-template-compiler": "^2.7.15",
"vuex-router-sync": "^5.0.0",
"vue-material-design-icons": "^5.2.0"
"vue-template-compiler": "^2.7.16",
"vuex": "^3.6.2",
"vuex-router-sync": "^5.0.0"
},
"devDependencies": {
"@nextcloud/babel-config": "^1.0.0",
"@nextcloud/browserslist-config": "^3.0.0",
"@nextcloud/eslint-config": "^8.3.0",
"@nextcloud/stylelint-config": "^2.3.1",
"@nextcloud/webpack-vue-config": "^6.0.0",
"eslint-webpack-plugin": "^4.0.1",
"stylelint-webpack-plugin": "^4.1.1"
"@nextcloud/babel-config": "^1.2.0",
"@nextcloud/browserslist-config": "^3.0.1",
"@nextcloud/eslint-config": "^8.4.1",
"@nextcloud/stylelint-config": "^3.0.1",
"@nextcloud/webpack-vue-config": "^6.1.1",
"eslint-webpack-plugin": "^4.2.0",
"stylelint-webpack-plugin": "^5.0.1"
},
"engines": {
"node": ">=16.0.0",
"npm": "^7.0.0 || ^8.0.0"
"node": ">=20.0.0",
"npm": "^10.0.0"
}
}
38 changes: 26 additions & 12 deletions src/components/settings/AdminSettings.vue
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@
{{ t('cloud_py_api', 'Cloud Python API') }}
</h2>
</div>
<div v-if="settings.length > 0" class="settings">
<NcLoadingIcon v-if="loadingSettings" :size="48" />
<div v-if="settings.length > 0 && !loadingSettings" class="settings">
<NcSettingsSection :name="t('cloud_py_api', mappedSettings.python_command.display_name)"
:description="t('cloud_py_api', mappedSettings.python_command.description)">
<input id="python_command"
@@ -79,7 +80,7 @@
</select>
</NcSettingsSection>
</div>
<div v-else>
<div v-if="settings.length === 0 && !loadingSettings">
<NcSettingsSection :name="t('cloud_py_api', 'Error')">
<NcEmptyContent style="margin-top: 0;"
:title="t('cloud_py_api', 'Settings list is empty')"
@@ -90,7 +91,7 @@
</NcEmptyContent>
</NcSettingsSection>
</div>
<NcSettingsSection :name="t('cloud_py_api', 'Bug report')">
<NcSettingsSection v-if="!loadingSettings" :name="t('cloud_py_api', 'Bug report')">
<BugReport />
</NcSettingsSection>
</div>
@@ -100,9 +101,14 @@
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import { showError, showSuccess } from '@nextcloud/dialogs'
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import { confirmPassword } from '@nextcloud/password-confirmation'
import {
NcSettingsSection,
NcCheckboxRadioSwitch,
NcEmptyContent,
NcLoadingIcon,
} from '@nextcloud/vue'
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
import BugReport from './BugReport.vue'
@@ -113,11 +119,13 @@ export default {
NcSettingsSection,
NcCheckboxRadioSwitch,
NcEmptyContent,
NcLoadingIcon,
BugReport,
AlertCircleOutline,
},
data() {
return {
loadingSettings: false,
settings: [],
mappedSettings: {},
remote_filesize_limit: null,
@@ -142,6 +150,7 @@ export default {
})
},
_getSettings() {
this.loadingSettings = true
axios.get(generateUrl('/apps/cloud_py_api/api/v1/settings')).then(res => {
this.settings = res.data
this.settings.forEach(setting => {
@@ -150,18 +159,23 @@ export default {
this.remote_filesize_limit = this.fromBytesToGBytes(Number(this.mappedSettings.remote_filesize_limit.value))
this.usePhpPathFromSettings = JSON.parse(this.mappedSettings.use_php_path_from_settings.value)
this.cpaLoglevel = JSON.parse(this.mappedSettings.cpa_loglevel.value)
}).finally(() => {
this.loadingSettings = false
})
},
saveChanges() {
this._updateSettings(this.settings).then(res => {
if (res.data.success) {
showSuccess(this.t('cloud_py_api', 'Settings successfully updated'))
}
})
.catch(err => {
confirmPassword().then(() => {
this._updateSettings(this.settings).then(res => {
if (res.data.success) {
showSuccess(this.t('cloud_py_api', 'Settings successfully updated'))
}
}).catch(err => {
console.debug(err)
showError(this.t('cloud_py_api', 'Some error occurred while updating settings'))
})
}).catch(() => {
showError(this.t('cloud_py_api', 'Password confirmation failed'))
})
},
fromBytesToGBytes(bytes) {
return (bytes / Math.pow(1024, 3)).toFixed(1)
4 changes: 1 addition & 3 deletions src/components/settings/BugReport.vue
Original file line number Diff line number Diff line change
@@ -27,8 +27,7 @@
<p style="margin: 0 0 20px;">
{{ t('mediadc', 'Collect non sensitive system info for bug report') }}
</p>
<NcButton class="mediadc-button-vue"
type="secondary"
<NcButton type="secondary"
:disabled="updating"
:aria-label="t('mediadc', 'Collect system info')"
@click="collectSystemInfo">
@@ -41,7 +40,6 @@
<h3>{{ t('mediadc', 'System info') }}</h3>
<NcButton v-if="systemInfo"
type="tertiary"
class="mediadc-button-vue"
@click="copySystemInfoToClipboard">
{{ t('mediadc', 'Copy to clipboard') }}
<template #icon>
7 changes: 5 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@

import { generateFilePath } from '@nextcloud/router'
import { getRequestToken } from '@nextcloud/auth'
import Nextcloudl10n from './mixins/Nextcludl10n.js'
import { translate, translatePlural } from '@nextcloud/l10n'

import Vue from 'vue'
import AdminSettings from './components/settings/AdminSettings.vue'
@@ -34,7 +34,10 @@ __webpack_nonce__ = btoa(getRequestToken())
// eslint-disable-next-line
__webpack_public_path__ = generateFilePath('cloud_py_api', '', 'js/')

Vue.mixin(Nextcloudl10n)
Vue.prototype.t = translate
Vue.prototype.n = translatePlural
Vue.prototype.OC = window.OC
Vue.prototype.OCA = window.OCA

const View = Vue.extend(AdminSettings)
new View().$mount('#cloud_py_api-admin-settings')
32 changes: 0 additions & 32 deletions src/mixins/Nextcludl10n.js

This file was deleted.