mirror of
https://github.com/chylex/Nextcloud-News.git
synced 2025-05-08 13:34:06 +02:00
Port admin settings to vue (#1880)
* Port admin settings to vue Co-authored-by: anoy. <anoymouserver@users.noreply.github.com> Co-authored-by: Benjamin Brahmer <info@b-brahmer.de> Signed-off-by: Carl Schwan <carl@carlschwan.eu>
This commit is contained in:
parent
39ac02c034
commit
753e88793e
.eslintrc.js
.github/workflows
CHANGELOG.mdMakefilebabel.config.jscss
js
lib
package-lock.jsonpackage.jsonsrc
stylelint.config.jstemplates
tests/Unit/Controller
webpack.config.js
7
.eslintrc.js
Normal file
7
.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-FileCopyrightText: Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'@nextcloud',
|
||||||
|
]
|
||||||
|
}
|
47
.github/workflows/lint-eslint.yml
vendored
Normal file
47
.github/workflows/lint-eslint.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# This workflow is provided via the organization template repository
|
||||||
|
#
|
||||||
|
# https://github.com/nextcloud/.github
|
||||||
|
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||||
|
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
name: eslint
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Read package.json node and npm engines version
|
||||||
|
uses: skjnldsv/read-package-engines-version-actions@v1.2
|
||||||
|
id: versions
|
||||||
|
with:
|
||||||
|
fallbackNode: '^12'
|
||||||
|
fallbackNpm: '^6'
|
||||||
|
|
||||||
|
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||||
|
|
||||||
|
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
|
||||||
|
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
@ -5,6 +5,7 @@ The format is mostly based on [Keep a Changelog](https://keepachangelog.com/en/1
|
|||||||
# Unreleased
|
# Unreleased
|
||||||
## [18.x.x]
|
## [18.x.x]
|
||||||
### Changed
|
### Changed
|
||||||
|
- Ported the admin settings to vue (#2353)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix PHP 8.1 deprecations (#1861)
|
- Fix PHP 8.1 deprecations (#1861)
|
||||||
|
4
Makefile
4
Makefile
@ -83,6 +83,8 @@ endif
|
|||||||
# Installs npm dependencies
|
# Installs npm dependencies
|
||||||
.PHONY: npm
|
.PHONY: npm
|
||||||
npm:
|
npm:
|
||||||
|
$(npm) ci
|
||||||
|
$(npm) run build
|
||||||
ifneq (, $(npm))
|
ifneq (, $(npm))
|
||||||
cd js && $(npm) run build
|
cd js && $(npm) run build
|
||||||
else
|
else
|
||||||
@ -171,7 +173,7 @@ appstore:
|
|||||||
# on macOS there is no option "--parents" for the "cp" command
|
# on macOS there is no option "--parents" for the "cp" command
|
||||||
mkdir -p $(appstore_sign_dir)/$(app_name)/js/build $(appstore_sign_dir)/$(app_name)/js/admin
|
mkdir -p $(appstore_sign_dir)/$(app_name)/js/build $(appstore_sign_dir)/$(app_name)/js/admin
|
||||||
cp js/build/app.min.js $(appstore_sign_dir)/$(app_name)/js/build
|
cp js/build/app.min.js $(appstore_sign_dir)/$(app_name)/js/build
|
||||||
cp js/admin/Admin.js $(appstore_sign_dir)/$(app_name)/js/admin
|
cp js/build/news-admin-settings.js* $(appstore_sign_dir)/$(app_name)/js/build
|
||||||
|
|
||||||
# export the key and cert to a file
|
# export the key and cert to a file
|
||||||
mkdir -p $(cert_dir)
|
mkdir -p $(cert_dir)
|
||||||
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// SPDX-FileCopyrightText: Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
const babelConfig = require('@nextcloud/babel-config')
|
||||||
|
|
||||||
|
module.exports = babelConfig
|
57
css/explore.css
Normal file
57
css/explore.css
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Nextcloud - News
|
||||||
|
*
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later. See the COPYING file.
|
||||||
|
*
|
||||||
|
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
||||||
|
* @copyright 2020, Jan C. Borchardt, https://jancborchardt.net
|
||||||
|
* @copyright Bernhard Posselt 2014
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Explore styles
|
||||||
|
*/
|
||||||
|
.explore #app-content-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#explore {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 45px 0 45px 45px;
|
||||||
|
}
|
||||||
|
#explore .grid-item {
|
||||||
|
width: 300px;
|
||||||
|
border: 2px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
margin: 0 24px 24px 0;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-title {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 center;
|
||||||
|
background-size: 24px;
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-title a {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-title a:hover, #explore .grid-item .explore-title a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-logo {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-logo img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#explore .grid-item .explore-subscribe {
|
||||||
|
margin-top: 16px;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=explore.css.map */
|
@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* Nextcloud - News
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later. See the COPYING file.
|
|
||||||
*
|
|
||||||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
||||||
* @copyright 2020, Jan C. Borchardt, https://jancborchardt.net
|
|
||||||
* @copyright Bernhard Posselt 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Explore styles
|
|
||||||
*/
|
|
||||||
.explore #app-content-wrapper {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#explore {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 45px 0 45px 45px;
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
width: 300px;
|
|
||||||
border: 2px solid var(--color-border);
|
|
||||||
border-radius: var(--border-radius-large);
|
|
||||||
margin: 0 24px 24px 0;
|
|
||||||
padding: 24px;
|
|
||||||
|
|
||||||
.explore-title {
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 0 center;
|
|
||||||
background-size: 24px;
|
|
||||||
padding-left: 32px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.explore-logo {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 25px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.explore-subscribe {
|
|
||||||
margin-top: 16px;
|
|
||||||
max-width: 100%;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
@media only screen and (max-width: $breakpoint_mobile) {
|
@media only screen and (max-width: 1024px) {
|
||||||
#app-content .utils .date {
|
#app-content .utils .date {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
@ -39,7 +39,7 @@ const sources = [
|
|||||||
'directive/**/*.js'
|
'directive/**/*.js'
|
||||||
];
|
];
|
||||||
const testSources = ['tests/**/*.js'];
|
const testSources = ['tests/**/*.js'];
|
||||||
const watchSources = sources.concat(testSources).concat(['*.js']);
|
const watchSources = sources.concat(testSources).concat(['*.js', '!news-admin-settings.js']);
|
||||||
const lintSources = watchSources;
|
const lintSources = watchSources;
|
||||||
|
|
||||||
// tasks
|
// tasks
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Nextcloud - News
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later. See the COPYING file.
|
|
||||||
*
|
|
||||||
* @author Alessandro Cosentino <cosenal@gmail.com>
|
|
||||||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
||||||
*
|
|
||||||
* @copyright 2012 Alessandro Cosentino
|
|
||||||
* @copyright 2012-2014 Bernhard Posselt
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace OCA\News\Controller;
|
|
||||||
|
|
||||||
use OCA\News\AppInfo\Application;
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
|
||||||
use OCP\IConfig;
|
|
||||||
use OCP\IRequest;
|
|
||||||
use OCP\AppFramework\Controller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AdminController
|
|
||||||
*
|
|
||||||
* @package OCA\News\Controller
|
|
||||||
*/
|
|
||||||
class AdminController extends Controller
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var IConfig
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AdminController constructor.
|
|
||||||
*
|
|
||||||
* @param IRequest $request The request
|
|
||||||
* @param IConfig $config Config for nextcloud
|
|
||||||
*/
|
|
||||||
public function __construct(IRequest $request, IConfig $config)
|
|
||||||
{
|
|
||||||
parent::__construct(Application::NAME, $request);
|
|
||||||
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller main entry.
|
|
||||||
*
|
|
||||||
* There are no checks for the index method since the output is
|
|
||||||
* rendered in admin/admin.php
|
|
||||||
*
|
|
||||||
* @return TemplateResponse
|
|
||||||
*/
|
|
||||||
public function index(): TemplateResponse
|
|
||||||
{
|
|
||||||
return new TemplateResponse($this->appName, 'admin', $this->getData(), 'blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get admin data.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getData(): array
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach (array_keys(Application::DEFAULT_SETTINGS) as $setting) {
|
|
||||||
$data[$setting] = $this->config->getAppValue(
|
|
||||||
Application::NAME,
|
|
||||||
$setting,
|
|
||||||
Application::DEFAULT_SETTINGS[$setting]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the app config.
|
|
||||||
*
|
|
||||||
* @param int $autoPurgeMinimumInterval New minimum interval for auto-purge
|
|
||||||
* @param int $autoPurgeCount New value of auto-purge count
|
|
||||||
* @param int $maxRedirects New value for max amount of redirects
|
|
||||||
* @param int $feedFetcherTimeout New timeout value for feed fetcher
|
|
||||||
* @param bool $useCronUpdates Whether or not to use cron updates
|
|
||||||
* @param string $exploreUrl URL to use for the explore feed
|
|
||||||
* @param int $updateInterval Interval in which the feeds will be updated
|
|
||||||
*
|
|
||||||
* @return array with the updated values
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
int $autoPurgeMinimumInterval,
|
|
||||||
int $autoPurgeCount,
|
|
||||||
int $maxRedirects,
|
|
||||||
int $feedFetcherTimeout,
|
|
||||||
bool $useCronUpdates,
|
|
||||||
string $exploreUrl,
|
|
||||||
int $updateInterval
|
|
||||||
): array {
|
|
||||||
$this->config->setAppValue($this->appName, 'autoPurgeMinimumInterval', $autoPurgeMinimumInterval);
|
|
||||||
$this->config->setAppValue($this->appName, 'autoPurgeCount', $autoPurgeCount);
|
|
||||||
$this->config->setAppValue($this->appName, 'maxRedirects', $maxRedirects);
|
|
||||||
$this->config->setAppValue($this->appName, 'feedFetcherTimeout', $feedFetcherTimeout);
|
|
||||||
$this->config->setAppValue($this->appName, 'useCronUpdates', $useCronUpdates);
|
|
||||||
$this->config->setAppValue($this->appName, 'exploreUrl', $exploreUrl);
|
|
||||||
$this->config->setAppValue($this->appName, 'updateInterval', $updateInterval);
|
|
||||||
|
|
||||||
return $this->getData();
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,7 @@ use OCA\News\AppInfo\Application;
|
|||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
|
use OCP\AppFramework\Services\IInitialState;
|
||||||
|
|
||||||
class AdminSettings implements ISettings
|
class AdminSettings implements ISettings
|
||||||
{
|
{
|
||||||
@ -14,25 +15,26 @@ class AdminSettings implements ISettings
|
|||||||
* @var IConfig
|
* @var IConfig
|
||||||
*/
|
*/
|
||||||
private $config;
|
private $config;
|
||||||
|
/** @var IInitialState */
|
||||||
|
private $initialState;
|
||||||
|
|
||||||
public function __construct(IConfig $config)
|
public function __construct(IConfig $config, IInitialState $initialState)
|
||||||
{
|
{
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
$this->initialState = $initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForm()
|
public function getForm()
|
||||||
{
|
{
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach (array_keys(Application::DEFAULT_SETTINGS) as $setting) {
|
foreach (array_keys(Application::DEFAULT_SETTINGS) as $setting) {
|
||||||
$data[$setting] = $this->config->getAppValue(
|
$this->initialState->provideInitialState($setting, $this->config->getAppValue(
|
||||||
Application::NAME,
|
Application::NAME,
|
||||||
$setting,
|
$setting,
|
||||||
Application::DEFAULT_SETTINGS[$setting]
|
(string)Application::DEFAULT_SETTINGS[$setting]
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TemplateResponse(Application::NAME, 'admin', $data);
|
return new TemplateResponse(Application::NAME, 'admin', []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSection()
|
public function getSection()
|
||||||
|
20794
package-lock.json
generated
Normal file
20794
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
package.json
Normal file
49
package.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "news",
|
||||||
|
"description": "News",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "Carl Schwan <carl@carlschwan.eu>",
|
||||||
|
"contributors": [],
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nextcloud/news/issues"
|
||||||
|
},
|
||||||
|
"license": "agpl",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --node-env production --progress",
|
||||||
|
"dev": "webpack --node-env development --progress",
|
||||||
|
"watch": "webpack --node-env development --progress --watch",
|
||||||
|
"serve": "webpack --node-env development serve --progress",
|
||||||
|
"lint": "eslint --ext .js,.vue src",
|
||||||
|
"lint:fix": "eslint --ext .js,.vue src --fix",
|
||||||
|
"stylelint": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue",
|
||||||
|
"stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix",
|
||||||
|
"sass": "sass css",
|
||||||
|
"sass:watch": "sass --watch css"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nextcloud/auth": "^2.0.0",
|
||||||
|
"@nextcloud/axios": "^1.10.0",
|
||||||
|
"@nextcloud/dialogs": "^3.1.4",
|
||||||
|
"@nextcloud/initial-state": "^2.0.0",
|
||||||
|
"@nextcloud/l10n": "^1.6.0",
|
||||||
|
"@nextcloud/password-confirmation": "^1.0.1",
|
||||||
|
"@nextcloud/router": "^2.0.0",
|
||||||
|
"@nextcloud/vue": "^6.0.0-beta.4",
|
||||||
|
"vue": "^2.7.0"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"extends @nextcloud/browserslist-config"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^16.0.0",
|
||||||
|
"npm": "^7.0.0 || ^8.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nextcloud/babel-config": "^1.0.0",
|
||||||
|
"@nextcloud/browserslist-config": "^2.2.0",
|
||||||
|
"@nextcloud/eslint-config": "^8.0.0",
|
||||||
|
"@nextcloud/stylelint-config": "^2.1.2",
|
||||||
|
"@nextcloud/webpack-vue-config": "^5.2.1"
|
||||||
|
}
|
||||||
|
}
|
147
src/components/AdminSettings.vue
Normal file
147
src/components/AdminSettings.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
SPDX-Licence-Identifier: AGPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NcSettingsSection :title="t('news', 'News')"
|
||||||
|
class="news-settings"
|
||||||
|
doc-url="https://nextcloud.github.io/news/admin/">
|
||||||
|
<NcCheckboxRadioSwitch type="switch"
|
||||||
|
:checked.sync="useCronUpdates"
|
||||||
|
@update:checked="update('useCronUpdates', useCronUpdates)">
|
||||||
|
{{ t('news', 'Use system cron for updates') }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<p><em>{{ t('news', 'Disable this if you use a custom updater.') }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="autoPurgeMinimumInterval"
|
||||||
|
:label="t('news', 'Purge interval')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('autoPurgeMinimumInterval', autoPurgeMinimumInterval)" />
|
||||||
|
<p><em>{{ t('news', 'Minimum amount of seconds after deleted feeds and folders are removed from the database; values below 60 seconds are ignored.') }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="autoPurgeCount"
|
||||||
|
:label="t('news', 'Maximum read count per feed')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('autoPurgeCount', autoPurgeCount)" />
|
||||||
|
<p><em>{{ t('news', `Defines the maximum amount of articles that can be read per feed which won't be deleted by the cleanup job; if old articles reappear after being read, increase this value; negative values such as -1 will turn this feature off.`) }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="maxRedirects"
|
||||||
|
:label="t('news', 'Maximum redirects')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('maxRedirects', maxRedirects)" />
|
||||||
|
<p><em>{{ t('news', 'How many redirects the feed fetcher should follow.') }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="feedFetcherTimeout"
|
||||||
|
:label="t('news', 'Feed fetcher timeout')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('feedFetcherTimeout', feedFetcherTimeout)" />
|
||||||
|
<p><em>{{ t('news', 'Maximum number of seconds to wait for an RSS or Atom feed to load; if it takes longer the update will be aborted.') }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="exploreUrl"
|
||||||
|
:label="t('news', 'Explore Service URL')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('exploreUrl', exploreUrl)" />
|
||||||
|
<p><em>{{ t('news', 'If given, this service\'s URL will be queried for displaying the feeds in the explore feed section. To fall back to the built in explore service, leave this input empty.') }}</em></p>
|
||||||
|
|
||||||
|
<NcTextField :value.sync="updateInterval"
|
||||||
|
:label="t('news', 'Update interval')"
|
||||||
|
:label-visible="true"
|
||||||
|
@update:value="update('updateInterval', updateInterval)" />
|
||||||
|
<p><em>{{ t('news', 'Interval in seconds in which the feeds will be updated.') }}</em></p>
|
||||||
|
</NcSettingsSection>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||||
|
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
|
||||||
|
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||||
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||||
|
import axios from '@nextcloud/axios'
|
||||||
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
|
import confirmPassword from '@nextcloud/password-confirmation'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param func
|
||||||
|
* @param wait
|
||||||
|
*/
|
||||||
|
function debounce(func, wait) {
|
||||||
|
let timeout
|
||||||
|
|
||||||
|
return function executedFunction(...args) {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(() => { func.apply(this, args) }, wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const successMessage = debounce(() => showSuccess(t('news', 'Successfuly updated news configuration')), 500)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AdminSettings',
|
||||||
|
components: {
|
||||||
|
NcCheckboxRadioSwitch,
|
||||||
|
NcSettingsSection,
|
||||||
|
NcTextField,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
useCronUpdates: loadState('news', 'useCronUpdates') === '1',
|
||||||
|
autoPurgeMinimumInterval: loadState('news', 'autoPurgeMinimumInterval'),
|
||||||
|
autoPurgeCount: loadState('news', 'autoPurgeCount'),
|
||||||
|
maxRedirects: loadState('news', 'maxRedirects'),
|
||||||
|
feedFetcherTimeout: loadState('news', 'feedFetcherTimeout'),
|
||||||
|
exploreUrl: loadState('news', 'exploreUrl'),
|
||||||
|
updateInterval: loadState('news', 'updateInterval'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async update(key, value) {
|
||||||
|
await confirmPassword()
|
||||||
|
const url = generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/{appId}/{key}', {
|
||||||
|
appId: 'news',
|
||||||
|
key,
|
||||||
|
})
|
||||||
|
if (key === 'useCronUpdates') {
|
||||||
|
value = value ? '1' : '0'
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post(url, {
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
this.handleResponse({
|
||||||
|
status: data.ocs?.meta?.status,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.handleResponse({
|
||||||
|
errorMessage: t('news', 'Unable to update news config'),
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleResponse({ status, errorMessage, error }) {
|
||||||
|
if (status !== 'ok') {
|
||||||
|
showError(errorMessage)
|
||||||
|
console.error(errorMessage, error)
|
||||||
|
} else {
|
||||||
|
successMessage()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.news-settings {
|
||||||
|
p {
|
||||||
|
max-width: 700px;
|
||||||
|
margin-top: .25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
20
src/main-admin.js
Normal file
20
src/main-admin.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-Licence-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { getRequestToken } from '@nextcloud/auth'
|
||||||
|
import { translate as t } from '@nextcloud/l10n'
|
||||||
|
|
||||||
|
import AdminSettings from './components/AdminSettings.vue'
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
__webpack_nonce__ = btoa(getRequestToken())
|
||||||
|
|
||||||
|
Vue.mixin({
|
||||||
|
methods: {
|
||||||
|
t,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const AdminSettingsView = Vue.extend(AdminSettings)
|
||||||
|
new AdminSettingsView().$mount('#vue-admin-news')
|
5
stylelint.config.js
Normal file
5
stylelint.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// SPDX-FileCopyrightText: Nextcloud contributors
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
const stylelintConfig = require('@nextcloud/stylelint-config')
|
||||||
|
|
||||||
|
module.exports = stylelintConfig
|
@ -1,132 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
script('news', 'admin/Admin');
|
declare(strict_types=1);
|
||||||
style('news', 'admin');
|
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-Licence-Identifier: AGPL-3.0-or-later
|
||||||
|
\OCP\Util::addScript('news', 'build/news-admin-settings');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="section" id="news">
|
<div id="vue-admin-news"></div>
|
||||||
<h2>News</h2>
|
|
||||||
<div class="form-line">
|
|
||||||
<p><input type="checkbox" name="news-use-cron-updates"
|
|
||||||
<?php if ($_['useCronUpdates']) p('checked'); ?>>
|
|
||||||
<label for="news-use-cron-updates">
|
|
||||||
<?php p($l->t('Use system cron for updates')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em><?php p($l->t(
|
|
||||||
'Disable this if you use a custom updater.'
|
|
||||||
)); ?></em>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-auto-purge-minimum-interval">
|
|
||||||
<?php p($l->t('Purge interval')); ?></p>
|
|
||||||
</label>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'Minimum amount of seconds after deleted feeds and folders ' .
|
|
||||||
'are removed from the database; values below 60 seconds are ' .
|
|
||||||
'ignored.'
|
|
||||||
)); ?></em>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-auto-purge-minimum-interval"
|
|
||||||
value="<?php p($_['autoPurgeMinimumInterval']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-auto-purge-count">
|
|
||||||
<?php p($l->t('Maximum read count per feed')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'Defines the maximum amount of articles that can be read per ' .
|
|
||||||
"feed which won't be deleted by the cleanup job; ".
|
|
||||||
'if old articles reappear after being read, increase ' .
|
|
||||||
'this value; negative values such as -1 will turn this ' .
|
|
||||||
'feature off.'
|
|
||||||
)); ?></em>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-auto-purge-count"
|
|
||||||
value="<?php p($_['autoPurgeCount']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-max-redirects">
|
|
||||||
<?php p($l->t('Maximum redirects')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'How many redirects the feed fetcher should follow.'
|
|
||||||
)); ?>
|
|
||||||
</em>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-max-redirects"
|
|
||||||
value="<?php p($_['maxRedirects']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-feed-fetcher-timeout">
|
|
||||||
<?php p($l->t('Feed fetcher timeout')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'Maximum number of seconds to wait for an RSS or Atom feed ' .
|
|
||||||
'to load; if it takes longer the update will be aborted.'
|
|
||||||
)); ?></em>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-feed-fetcher-timeout"
|
|
||||||
value="<?php p($_['feedFetcherTimeout']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-explore-url">
|
|
||||||
<?php p($l->t('Explore Service URL')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'If given, this service\'s URL will be queried for ' .
|
|
||||||
'displaying the feeds in the explore feed section. To ' .
|
|
||||||
'fall back to the built in explore service, leave this ' .
|
|
||||||
'input empty.'
|
|
||||||
)); ?>
|
|
||||||
</em>
|
|
||||||
<a href="https://nextcloud.github.io/news/admin/"><?php p($l->t(
|
|
||||||
'For more information check the wiki.'
|
|
||||||
)); ?></a>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-explore-url"
|
|
||||||
value="<?php p($_['exploreUrl']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div class="form-line">
|
|
||||||
<p>
|
|
||||||
<label for="news-updater-interval">
|
|
||||||
<?php p($l->t('Update interval')); ?>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
<?php p($l->t(
|
|
||||||
'Interval in seconds in which the feeds will be updated.'
|
|
||||||
)); ?>
|
|
||||||
</em>
|
|
||||||
<a href="https://nextcloud.github.io/news/admin/"><?php p($l->t(
|
|
||||||
'For more information check the documentation.'
|
|
||||||
)); ?></a>
|
|
||||||
</p>
|
|
||||||
<p><input type="text" name="news-update-interval"
|
|
||||||
value="<?php p($_['updateInterval']); ?>"></p>
|
|
||||||
</div>
|
|
||||||
<div id="news-saved-message">
|
|
||||||
<span class="msg success"><?php p($l->t('Saved')); ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Nextcloud - News
|
|
||||||
*
|
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
|
||||||
* later. See the COPYING file.
|
|
||||||
*
|
|
||||||
* @author Alessandro Cosentino <cosenal@gmail.com>
|
|
||||||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
||||||
* @copyright 2012 Alessandro Cosentino
|
|
||||||
* @copyright 2012-2014 Bernhard Posselt
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace OCA\News\Tests\Unit\Controller;
|
|
||||||
|
|
||||||
use OCA\News\Controller\AdminController;
|
|
||||||
use OCA\News\Service\ItemService;
|
|
||||||
use OCP\IConfig;
|
|
||||||
use OCP\IRequest;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class AdminControllerTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $appName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \PHPUnit\Framework\MockObject\MockObject|IRequest
|
|
||||||
*/
|
|
||||||
private $request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var AdminController
|
|
||||||
*/
|
|
||||||
private $controller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \PHPUnit\Framework\MockObject\MockObject|IConfig
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \PHPUnit\Framework\MockObject\MockObject|ItemService
|
|
||||||
*/
|
|
||||||
private $itemService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets run before each test
|
|
||||||
*/
|
|
||||||
public function setUp(): void
|
|
||||||
{
|
|
||||||
$this->appName = 'news';
|
|
||||||
$this->request = $this->getMockBuilder(IRequest::class)
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
$this->config = $this->getMockBuilder(IConfig::class)
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
$this->itemService = $this->getMockBuilder(ItemService::class)
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->controller = new AdminController($this->request, $this->config, $this->itemService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test \OCA\News\Controller\AdminController::index
|
|
||||||
*/
|
|
||||||
public function testIndex()
|
|
||||||
{
|
|
||||||
$expected = [
|
|
||||||
'autoPurgeMinimumInterval' => 1,
|
|
||||||
'autoPurgeCount' => 2,
|
|
||||||
'maxRedirects' => 3,
|
|
||||||
'feedFetcherTimeout' => 4,
|
|
||||||
'useCronUpdates' => false,
|
|
||||||
'exploreUrl' => 'test',
|
|
||||||
'updateInterval' => 3601
|
|
||||||
];
|
|
||||||
$map = [
|
|
||||||
['news','autoPurgeMinimumInterval', 60, 1],
|
|
||||||
['news','autoPurgeCount', 200, 2],
|
|
||||||
['news','maxRedirects', 10, 3],
|
|
||||||
['news','feedFetcherTimeout', 60, 4],
|
|
||||||
['news','useCronUpdates', true, false,],
|
|
||||||
['news','exploreUrl', '', 'test'],
|
|
||||||
['news','updateInterval', 3600, 3601]
|
|
||||||
];
|
|
||||||
$this->config->expects($this->exactly(count($map)))
|
|
||||||
->method('getAppValue')
|
|
||||||
->will($this->returnValueMap($map));
|
|
||||||
|
|
||||||
$response = $this->controller->index();
|
|
||||||
$data = $response->getParams();
|
|
||||||
$name = $response->getTemplateName();
|
|
||||||
$type = $response->getRenderAs();
|
|
||||||
|
|
||||||
$this->assertEquals($type, 'blank');
|
|
||||||
$this->assertEquals($name, 'admin');
|
|
||||||
$this->assertEquals($expected, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function testUpdate()
|
|
||||||
{
|
|
||||||
$expected = [
|
|
||||||
'autoPurgeMinimumInterval' => 1,
|
|
||||||
'autoPurgeCount' => 2,
|
|
||||||
'maxRedirects' => 3,
|
|
||||||
'feedFetcherTimeout' => 4,
|
|
||||||
'useCronUpdates' => false,
|
|
||||||
'exploreUrl' => 'test',
|
|
||||||
'updateInterval' => 3601
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->config->expects($this->exactly(count($expected)))
|
|
||||||
->method('setAppValue')
|
|
||||||
->withConsecutive(
|
|
||||||
['news','autoPurgeMinimumInterval', 1],
|
|
||||||
['news','autoPurgeCount', 2],
|
|
||||||
['news','maxRedirects', 3],
|
|
||||||
['news','feedFetcherTimeout', 4],
|
|
||||||
['news','useCronUpdates', false],
|
|
||||||
['news','exploreUrl', 'test'],
|
|
||||||
['news','updateInterval', 3601]
|
|
||||||
);
|
|
||||||
|
|
||||||
$map = [
|
|
||||||
['news','autoPurgeMinimumInterval', 60, 1],
|
|
||||||
['news','autoPurgeCount', 200, 2],
|
|
||||||
['news','maxRedirects', 10, 3],
|
|
||||||
['news','feedFetcherTimeout', 60, 4],
|
|
||||||
['news','useCronUpdates', true, false,],
|
|
||||||
['news','exploreUrl', '', 'test'],
|
|
||||||
['news','updateInterval', 3600, 3601]
|
|
||||||
];
|
|
||||||
$this->config->expects($this->exactly(count($map)))
|
|
||||||
->method('getAppValue')
|
|
||||||
->will($this->returnValueMap($map));
|
|
||||||
|
|
||||||
$response = $this->controller->update(
|
|
||||||
$expected['autoPurgeMinimumInterval'],
|
|
||||||
$expected['autoPurgeCount'],
|
|
||||||
$expected['maxRedirects'],
|
|
||||||
$expected['feedFetcherTimeout'],
|
|
||||||
$expected['useCronUpdates'],
|
|
||||||
$expected['exploreUrl'],
|
|
||||||
$expected['updateInterval']
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $response);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
13
webpack.config.js
Normal file
13
webpack.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-FileCopyrightText: Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
|
||||||
|
webpackConfig.entry = {
|
||||||
|
'admin-settings': path.join(__dirname, 'src', 'main-admin.js'),
|
||||||
|
}
|
||||||
|
webpackConfig.output.path = path.resolve('./js/build/')
|
||||||
|
webpackConfig.output.publicPath = path.join('/apps/', process.env.npm_package_name, '/js/build/')
|
||||||
|
|
||||||
|
module.exports = webpackConfig
|
Loading…
Reference in New Issue
Block a user