mirror of
https://github.com/chylex/Nextcloud-News.git
synced 2025-04-08 07:15:43 +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
|
||||
## [18.x.x]
|
||||
### Changed
|
||||
- Ported the admin settings to vue (#2353)
|
||||
|
||||
### Fixed
|
||||
- Fix PHP 8.1 deprecations (#1861)
|
||||
|
4
Makefile
4
Makefile
@ -83,6 +83,8 @@ endif
|
||||
# Installs npm dependencies
|
||||
.PHONY: npm
|
||||
npm:
|
||||
$(npm) ci
|
||||
$(npm) run build
|
||||
ifneq (, $(npm))
|
||||
cd js && $(npm) run build
|
||||
else
|
||||
@ -171,7 +173,7 @@ appstore:
|
||||
# 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
|
||||
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
|
||||
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 {
|
||||
display: none;
|
||||
}
|
@ -39,7 +39,7 @@ const sources = [
|
||||
'directive/**/*.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;
|
||||
|
||||
// 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\IConfig;
|
||||
use OCP\Settings\ISettings;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
|
||||
class AdminSettings implements ISettings
|
||||
{
|
||||
@ -14,25 +15,26 @@ class AdminSettings implements ISettings
|
||||
* @var IConfig
|
||||
*/
|
||||
private $config;
|
||||
/** @var IInitialState */
|
||||
private $initialState;
|
||||
|
||||
public function __construct(IConfig $config)
|
||||
public function __construct(IConfig $config, IInitialState $initialState)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->initialState = $initialState;
|
||||
}
|
||||
|
||||
public function getForm()
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach (array_keys(Application::DEFAULT_SETTINGS) as $setting) {
|
||||
$data[$setting] = $this->config->getAppValue(
|
||||
$this->initialState->provideInitialState($setting, $this->config->getAppValue(
|
||||
Application::NAME,
|
||||
$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()
|
||||
|
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
|
||||
script('news', 'admin/Admin');
|
||||
style('news', 'admin');
|
||||
declare(strict_types=1);
|
||||
// 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">
|
||||
<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>
|
||||
<div id="vue-admin-news"></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