Add ncp-community.sh and admin section to ncp-app

Signed-off-by: Tobias K <6317548+theCalcaholic@users.noreply.github.com>
This commit is contained in:
Tobias K 2023-10-19 20:17:37 +02:00
parent 50c09041dc
commit 3958883e62
No known key found for this signature in database
GPG Key ID: 44FD368932E645C1
39 changed files with 20098 additions and 23 deletions

View File

@ -16,7 +16,9 @@ git clone -b "$BRANCH" --depth 20 -q --bare https://github.com/nextcloud/nextclo
cd "$TEMPDIR" || exit 1
VER=$( git describe --always --tags | grep -oP "v\d+\.\d+\.\d+" )
[[ -f "/usr/local/etc/instance.cfg" ]] && {
canary="$(get_app_param ncp-community.sh CANARY)"
[[ "$canary" != "yes" ]] && [[ -f "/usr/local/etc/instance.cfg" ]] && {
cohorte_id="$(jq .cohorteId /usr/local/etc/instance.cfg)"
[[ -f "./staged_rollouts/${VER}.txt" ]] && {
grep "^${cohorte_id}$" "./staged_rollouts/${VER}.txt" || {

View File

@ -77,7 +77,6 @@ fi
cat > /usr/local/etc/instance.cfg <<EOF
{
"cohorteId": ${cohorte_id},
"canary": false
}
EOF
}

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Configure various settings for community participation
install() { :; }
configure() { :; }

View File

@ -212,6 +212,29 @@ function find_app_param_num()
}
function get_app_params() {
local script="${1?}"
local cfg_file="${CFGDIR}/${script%.sh}.cfg"
[[ -f "$cfg_file" ]] && {
local cfg="$( cat "$cfg_file" )"
local param_count="$(jq ".params | length" <<<"$cfg")"
local i=0
local json="{"$'\n'
while [[ $i -lt $param_count ]]
do
param_id="$(jq -r ".params[$i].id" <<<"$cfg")"
param_val="$(jq -r ".params[$i].value" <<<"$cfg")"
json="${json} \"${param_id}\": \"${param_val}\","$'\n'
i=$((i+1))
done
json="${json}}"
echo "$json"
return 0
}
return 1
}
install_template() {
local template="${1?}"
local target="${2?}"

View File

@ -0,0 +1,35 @@
{
"id": "ncp-community",
"name": "NCP Community",
"title": "NCP Community Settings",
"description": "Configure various NCP community options",
"info": "",
"infotitle": "",
"params": [
{
"id": "CANARY",
"name": "Enable canary (testing) channel for updates",
"value": "no",
"type": "bool"
},
{
"id": "ADMIN_NOTIFICATIONS",
"name": "Enable notifications about changes in NCP",
"value": "yes",
"type": "bool"
},
{
"id": "USAGE_SURVEYS",
"name": "Help me improve NCP by participating in occasional usage surveys",
"value": "no",
"type": "bool"
},
{
"id": "NOTIFICATION_ACCOUNTS",
"name": "Limit notifications to these accounts",
"value": "",
"suggest": "Comma separated list of nextcloud accounts",
"type": "text"
}
]
}

View File

@ -84,14 +84,6 @@ rm /.ncp-image
cd -
rm -rf "${TEMPDIR}"
cohorte_id=$((RANDOM % 100))
cat <<EOF > /usr/local/etc/instance.cfg
{
"cohorteId": ${cohorte_id},
"canary": false
}
EOF
IP="$(get_ip)"
echo "Done.

7
ncp-app/.eslintrc.js Normal file
View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
module.exports = {
extends: [
'@nextcloud',
]
}

3
ncp-app/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
# SPDX-License-Identifier: AGPL-3.0-or-later
/js/* binary

9
ncp-app/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
# SPDX-License-Identifier: AGPL-3.0-or-later
.idea
*.iml
/vendor/
/build/
node_modules/
/.php_cs.cache
js/*hot-update.*

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
->ignoreVCSIgnored(true)
->notPath('build')
->notPath('l10n')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;

164
ncp-app/Makefile Normal file
View File

@ -0,0 +1,164 @@
# SPDX-FileCopyrightText: Bernhard Posselt <dev@bernhard-posselt.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
# Generic Makefile for building and packaging a Nextcloud app which uses npm and
# Composer.
#
# Dependencies:
# * make
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * npm: for building and testing everything JS
#
# If no composer.json is in the app root directory, the Composer step
# will be skipped. The same goes for the package.json which can be located in
# the app root or the js/ directory.
#
# The npm command by launches the npm build script:
#
# npm run build
#
# The npm test command launches the npm test script:
#
# npm run test
#
# The idea behind this is to be completely testing and build tool agnostic. All
# build tools and additional package managers should be installed locally in
# your project, since this won't pollute people's global namespace.
#
# The following npm scripts in your package.json install and update the bower
# and npm dependencies and use gulp as build system (notice how everything is
# run from the node_modules folder):
#
# "scripts": {
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },
app_name=$(notdir $(CURDIR))
build_tools_directory=$(CURDIR)/build/tools
source_build_directory=$(CURDIR)/build/artifacts/source
source_package_name=$(source_build_directory)/$(app_name)
appstore_build_directory=$(CURDIR)/build/artifacts
appstore_package_name=$(appstore_build_directory)/$(app_name)
npm=$(shell which npm 2> /dev/null)
composer=$(shell which composer 2> /dev/null)
all: build
# Fetches the PHP and JS dependencies and compiles the JS. If no composer.json
# is present, the composer step is skipped, if no package.json or js/package.json
# is present, the npm step is skipped
.PHONY: build
build:
ifneq (,$(wildcard $(CURDIR)/composer.json))
make composer
endif
ifneq (,$(wildcard $(CURDIR)/package.json))
make npm
endif
ifneq (,$(wildcard $(CURDIR)/js/package.json))
make npm
endif
cp js-src/*.js js/
# Installs and updates the composer dependencies. If composer is not installed
# a copy is fetched from the web
.PHONY: composer
composer:
ifeq (, $(composer))
@echo "No composer command available, downloading a copy from the web"
mkdir -p $(build_tools_directory)
curl -sS https://getcomposer.org/installer | php
mv composer.phar $(build_tools_directory)
php $(build_tools_directory)/composer.phar install --prefer-dist
else
composer install --prefer-dist
endif
# Installs npm dependencies
.PHONY: npm
npm:
ifeq (,$(wildcard $(CURDIR)/package.json))
cd js && $(npm) run build
else
npm run build
endif
# Removes the appstore build
.PHONY: clean
clean:
rm -rf ./build
# Same as clean but also removes dependencies installed by composer, bower and
# npm
.PHONY: distclean
distclean: clean
rm -rf vendor
rm -rf node_modules
rm -rf js/vendor
rm -rf js/node_modules
# Builds the source and appstore package
.PHONY: dist
dist:
make source
make appstore
# Builds the source package
.PHONY: source
source:
rm -rf $(source_build_directory)
mkdir -p $(source_build_directory)
tar cvzf $(source_package_name).tar.gz \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/node_modules" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/js/*.log" \
../$(app_name) \
# Builds the source package for the app store, ignores php tests, js tests
# and build related folders that are unnecessary for an appstore release
.PHONY: appstore
appstore:
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
tar cvzf $(appstore_package_name).tar.gz \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/tests" \
--exclude="../$(app_name)/Makefile" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/phpunit*xml" \
--exclude="../$(app_name)/composer.*" \
--exclude="../$(app_name)/node_modules" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/js/tests" \
--exclude="../$(app_name)/js/test" \
--exclude="../$(app_name)/js/*.log" \
--exclude="../$(app_name)/js/package.json" \
--exclude="../$(app_name)/js/bower.json" \
--exclude="../$(app_name)/js/karma.*" \
--exclude="../$(app_name)/js/protractor.*" \
--exclude="../$(app_name)/package.json" \
--exclude="../$(app_name)/bower.json" \
--exclude="../$(app_name)/karma.*" \
--exclude="../$(app_name)/protractor\.*" \
--exclude="../$(app_name)/.*" \
--exclude="../$(app_name)/js/.*" \
--exclude="../$(app_name)/webpack.config.js" \
--exclude="../$(app_name)/stylelint.config.js" \
--exclude="../$(app_name)/CHANGELOG.md" \
--exclude="../$(app_name)/README.md" \
--exclude="../$(app_name)/package-lock.json" \
--exclude="../$(app_name)/LICENSES" \
../$(app_name) \
.PHONY: test
test: composer
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.xml
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml

View File

@ -2,17 +2,17 @@
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>nextcloudpi</id>
<name>NextCloudPi</name>
<name>NextcloudPi</name>
<summary>Nextcloud management tools</summary>
<description><![CDATA[NextCloudPi features a preconfigured Nextcloud instance and a complete set of tools around it for easy management.]]></description>
<version>0.0.1</version>
<version>0.0.2</version>
<licence>agpl</licence>
<author mail="nachoparker@ownyourbits.com" homepage="https://ownyourbits.com">nachoparker</author>
<namespace>NextCloudPi</namespace>
<namespace>NextcloudPi</namespace>
<category>tools</category>
<bugs>https://github.com/nextcloud/nextcloudpi/issues</bugs>
<dependencies>
<nextcloud min-version="14" max-version="27"/>
<nextcloud min-version="22" max-version="27"/>
</dependencies>
<navigations>
<navigation>
@ -20,4 +20,7 @@
<route>nextcloudpi.page.index</route>
</navigation>
</navigations>
<settings>
<admin>OCA\NextcloudPi\Settings\AdminSettings</admin>
</settings>
</info>

View File

@ -2,13 +2,14 @@
/**
* Create your routes in here. The name is the lowercase name of the controller
* without the controller part, the stuff after the hash is the method.
* e.g. page#index -> OCA\NextCloudPi\Controller\PageController->index()
* e.g. page#index -> OCA\NextcloudPi\Controller\PageController->index()
*
* The controller class has to be registered in the application.php file since
* it's instantiated in there
*/
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
]
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'settings#save', 'url' => '/api/settings', 'verb' => 'POST']
]
];

5
ncp-app/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
const babelConfig = require('@nextcloud/babel-config')
module.exports = babelConfig

38
ncp-app/composer.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "nextcloud/nextcloudpi",
"description": "NextcloudPi App",
"type": "project",
"license": "AGPL-3.0-or-later",
"authors": [
{
"name": "Tobias Knöppler"
}
],
"require-dev": {
"phpunit/phpunit": "^9",
"sabre/dav": "^4.1",
"sabre/xml": "^2.2",
"symfony/event-dispatcher": "^5.3.11",
"christophwurst/nextcloud": "dev-master@dev",
"psalm/phar": "^4.10",
"nextcloud/coding-standard": "^1.0"
},
"scripts": {
"lint": "find nc-app/nextcloudpi -name \\\\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm.phar --threads=1",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
"psalm:clear": "psalm.phar --clear-cache && psalm --clear-global-cache",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
},
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true
},
"platform": {
"php": "7.4"
}
}
}

2839
ncp-app/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

38
ncp-app/css/admin.css Normal file
View File

@ -0,0 +1,38 @@
#nextcloudpi li {
display: flex;
flex-direction: row;
justify-content: start;
margin-left: 2em;
}
#nextcloudpi li div:first-of-type {
width: 10em;
white-space: nowrap;
}
#nextcloudpi li * {
line-height: 36px;
}
#nextcloudpi hidden {
display: none;
}
#nextcloudpi .error-message {
padding: 1em;
color: red;
border-width: 1px;
border-color: red;
border-style: dashed;
}
#nextcloudpi input[name="notificationAccounts"] {
width: 27em;
}
.divider {
height: .5em;
border-top-width: 1px;
border-top-style: dotted;
border-top-color: var(--color-main-text);
}

6
ncp-app/js-src/script.js Normal file
View File

@ -0,0 +1,6 @@
// open the NCP web panel
var url = window.location.protocol + '//' + window.location.hostname + ':4443';
if ( !window.open( url, '_blank' ) ) // try to open in a new tab first
window.location.href = url;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
namespace OCA\NextcloudPi\AppInfo;
use OCP\AppFramework\App;
class Application extends App {
public const APP_ID = 'nextcloudpi';
public function __construct() {
parent::__construct(self::APP_ID);
}
}

View File

@ -1,5 +1,5 @@
<?php
namespace OCA\NextCloudPi\Controller;
namespace OCA\NextcloudPi\Controller;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace OCA\NextcloudPi\Controller;
use OCA\NextcloudPi\Exceptions\InvalidSettingsException;
use OCA\NextcloudPi\Exceptions\SaveSettingsException;
use OCA\NextcloudPi\Service\SettingsService;
use OCP\IRequest;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Controller;
class SettingsController extends Controller {
/** @var SettingsService */
private $service;
/**
* SettingsController constructor
* @param SettingsService $service
*/
public function __construct(SettingsService $service) {
$this->service = $service;
}
/**
* @NoCSRFRequired
* @CORS
*
* @param array $settings
*/
public function save(array $settings): JSONResponse {
try {
$this->service->saveSettings($settings);
return new JSONResponse([]);
} catch(InvalidSettingsException $e) {
return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_BAD_REQUEST);
} catch(SaveSettingsException $e) {
return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
}
?>

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace OCA\NextcloudPi\Exceptions;
use Exception;
class InvalidSettingsException extends Exception {
}
?>

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace OCA\NextcloudPi\Exceptions;
use Exception;
class SaveSettingsException extends Exception {
}
?>

View File

@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace OCA\NextcloudPi\Service;
use OCA\NextcloudPi\Exceptions\InvalidSettingsException;
use OCA\NextcloudPi\Exceptions\SaveSettingsException;
class SettingsService {
/**
* @param $name string of the config
* @param array $defaults Default value to use if the config can't be loaded
* @return array
*/
public function getConfig(string $name, array $defaults): array
{
[$ret, $config_str, $stderr] = $this->runCommand( "bash -c \"sudo /home/www/ncp-app-bridge.sh config $name\"");
$config = null;
if ($ret == 0) {
$config = json_decode($config_str, true);
}
if ($config == null) {
error_log("Failed to retrieve ncp config (exit code: $ret)");
return $defaults;
}
return $config;
}
/**
* @param $name string of the config
* @param string $defaults Default value to use if the file can't be loaded
* @return string
*/
public function getFileContent(string $name, string $defaults): string
{
[$ret, $file_contents, $stderr] = $this->runCommand( "bash -c \"sudo /home/www/ncp-app-bridge.sh file $name\"");
if ($ret != 0) {
return $defaults;
}
return $file_contents;
}
/**
* @throws InvalidSettingsException
* @throws SaveSettingsException
*/
public function saveSettings(array $settings) {
$parseBool = function ($val): string {
return $val ? "yes" : "no";
};
$identityFn = function ($val) {
return $val;
};
$settings_map = [
"CANARY" => ["ncp-community", "CANARY", $parseBool],
"ADMIN_NOTIFICATIONS" => ["ncp-community", "ADMIN_NOTIFICATIONS", $parseBool],
"USAGE_SURVEYS" => ["ncp-community", "USAGE_SURVEYS", $parseBool],
"NOTIFICATION_ACCOUNTS" => ["ncp-community", "NOTIFICATION_ACCOUNTS", $identityFn]
];
foreach ($settings as $k => $value) {
[$cfgName, $fieldName, $fn] = $settings_map[$k];
if ($cfgName == null || $fieldName == null) {
throw new InvalidSettingsException("key error for '$k'");
}
$parsed = $fn($value);
$cmd = "bash -c \"sudo /home/www/ncp-app-bridge.sh config '$cfgName' '$fieldName=$parsed'\"";
[$ret, $stdout, $stderr] = $this->runCommand($cmd);
if ($ret !== 0) {
throw new SaveSettingsException(
"Failed to save NCP settings '$cfgName/$fieldName': \n error output from command:\n\n$cmd"
. str_replace("\n", "\n> ", $stderr));
}
}
}
private function runCommand(string $cmd): array {
$descriptorSpec = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$proc = proc_open($cmd, $descriptorSpec, $pipes, "/home/www-data", null);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
return [proc_close($proc), $stdout, $stderr];
}
}
?>

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace OCA\NextcloudPi\Settings;
use OCA\NextcloudPi\Service\SettingsService;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Settings\ISettings;
class AdminSettings implements ISettings {
/** @var SettingsService */
private $service;
/**
* AdminSettings constructor
* @param SettingsService $service
*/
public function __construct(SettingsService $service) {
$this->service = $service;
}
/**
* @return TemplateResponse
*/
public function getForm() {
$ncp_config = $this->service->getConfig("ncp",
["nextcloud_version" => "unknown", "php_version" => "unknown", "release" => "unknown"]);
$community_config = $this->service->getConfig("ncp-community",
[
"CANARY" => 'no',
"USAGE_SURVEYS" => 'no',
"ADMIN_NOTIFICATIONS" => 'no',
"NOtIFICATION_ACCOUNTS" => ""
]);
$ncp_version = trim($this->service->getFileContent("ncp-version", "unknown"));
return new TemplateResponse('nextcloudpi', 'admin', [
'community' => $community_config,
'ncp' => $ncp_config,
'ncp_version' => $ncp_version
]);
}
/**
* @return string
*/
public function getSection() {
return "server";
}
/**
* @return int
*/
public function getPriority() {
return 1;
}
}
?>

BIN
ncp-app/ncp.tar.gz Normal file

Binary file not shown.

16325
ncp-app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

43
ncp-app/package.json Normal file
View File

@ -0,0 +1,43 @@
{
"name": "nextcloudpi",
"description": "NextcloudPi App",
"version": "0.0.1",
"author": "Tobias Knöppler <tobias@knoeppler.net>",
"contributors": [],
"bugs": {
"url": "https://github.com/nextcloud/nextcloudpi"
},
"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"
},
"dependencies": {
"@nextcloud/axios": "^1.10.0",
"@nextcloud/dialogs": "^3.1.4",
"@nextcloud/router": "^2.0.0",
"@nextcloud/vue": "^5.4.0",
"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"
}
}

38
ncp-app/psalm.xml Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<psalm
errorLevel="4"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="tests/psalm-baseline.xml"
>
<!--
SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
SPDX-License-Identifier: CC0-1.0
-->
<projectFiles>
<directory name="lib" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<extraFiles>
<directory name="vendor" />
<ignoreFiles>
<directory name="vendor/phpunit/php-code-coverage" />
<directory name="vendor/psalm" />
</ignoreFiles>
</extraFiles>
<issueHandlers>
<UndefinedDocblockClass>
<errorLevel type="suppress">
<referencedClass name="OC\AppFramework\OCS\BaseResponse"/>
<referencedClass name="Doctrine\DBAL\Schema\Schema" />
<referencedClass name="Doctrine\DBAL\Schema\SchemaException" />
<referencedClass name="Doctrine\DBAL\Driver\Statement" />
<referencedClass name="Doctrine\DBAL\Schema\Table" />
</errorLevel>
</UndefinedDocblockClass>
</issueHandlers>
</psalm>

66
ncp-app/src/main-admin.js Normal file
View File

@ -0,0 +1,66 @@
/**
* SPDX-FileCopyrightText: 2018 John Molakvoæ <skjnldsv@protonmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import {generateFilePath, generateUrl} from '@nextcloud/router'
import axios from '@nextcloud/axios'
//
// import Vue from 'vue'
// import App from './App.vue'
// eslint-disable-next-line
__webpack_public_path__ = generateFilePath(appName, '', 'js/')
//
// Vue.mixin({ methods: { t, n } })
//
// export default new Vue({
// el: '#nextcloudpi',
// render: h => h(App),
// })
async function saveSettings() {
let settings = collectSettings();
console.log("Saving nextcloudpi settings: ", settings);
try {
let response = await axios.post(generateUrl('/apps/nextcloudpi/api/settings'), {settings: settings})
console.log("Saving ncp settings succeeded")
return {success: true, error: null}
} catch (e) {
// console.log("axios failure: ", arguments)
console.error(e)
let errMsg = e.response.data.error;
throw Error(`${errMsg ? errMsg : e.message} (HTTP ${e.response.status})`)
}
}
function collectSettings() {
let settings = {};
document.querySelectorAll("#nextcloudpi input").forEach(element => {
if (element.type === "checkbox") {
settings[element.name] = element.checked;
} else {
settings[element.name] = element.value;
}
});
return settings;
}
window.addEventListener('load', () => {
console.log("Listening to ncp settings changes");
let errorBox = document.querySelector("#nextcloudpi .error-message");
document.querySelectorAll("#nextcloudpi input").forEach(element => {
element.addEventListener("change", async () => {
saveSettings()
.then(() => {
errorBox.classList.add("hidden");
})
.catch(e => {
console.error(e);
errorBox.innerText = "Failed to save NextcloudPi settings: " + e.message;
errorBox.classList.remove("hidden");
})
})
})
})

View 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

View File

@ -0,0 +1,42 @@
<?php
script('nextcloudpi', 'nextcloudpi-admin');
style('nextcloudpi', 'admin');
?>
<div id="nextcloudpi" class="section">
<h2>NextcloudPi</h2>
<h3>System Info</h3>
<ul>
<li>
<div>NCP Version:</div><div><?php echo $_["ncp_version"] ?></div>
</li>
<li>
<div>PHP Version:</div><div><?php echo $_["ncp"]["php_version"] ?></div>
</li>
<li>
<div>Debian Release:</div><div><?php echo $_["ncp"]["release"] ?></div>
</li>
</ul>
<h3>Settings</h3>
<ul>
<li>
<input name="canary" type="checkbox" <?php echo $_['community']['CANARY'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
<label for="canary">Enable updates from canary (testing) channel</label>
</li>
<li>
<input name="adminNotifications" type="checkbox" <?php echo $_['community']['ADMIN_NOTIFICATIONS'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
<label for="adminNotifications">Enable notifications about relevant changes in NCP</label>
</li>
<li>
<input name="adminNotifications" type="checkbox" <?php echo $_['community']['USAGE_SURVEYS'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
<label for="adminNotifications">Enable notifications for surveys that help to improve NCP</label>
</li>
<li>
<div>Accounts to notify:</div>
<input type="text" name="notificationAccounts"
placeholder="comma separated list of accounts. Default is: all admins"
value="<?php echo $_['community']['NOTIFICATION_ACCOUNTS']; ?>"/>
</li>
</ul>
<p class="error-message hidden"></p>
</div>

12
ncp-app/webpack.config.js Normal file
View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
const path = require('path')
const webpackConfig = require('@nextcloud/webpack-vue-config')
module.exports = {...webpackConfig,
...{
entry: {
admin: path.join(__dirname, 'src/main-admin')
}
}
}

View File

@ -136,7 +136,7 @@ HTML;
<div id="header-left">
<a href="https://nextcloudpi.com" id="nextcloudpi" target="_blank" tabindex="1">
<div class="logo-icon">
<h1 class="hidden-visually">NextCloudPi</h1>
<h1 class="hidden-visually">NextcloudPi</h1>
</div>
</a>
<a id=versionlink target="_blank" href="https://github.com/nextcloud/nextcloudpi/blob/master/changelog.md">

56
ncp.sh
View File

@ -176,7 +176,61 @@ grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
tar $pigz -tf "$file" data &>/dev/null
EOF
chmod 700 /home/www/ncp-backup-launcher.sh
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
cat > /home/www/ncp-app-bridge.sh <<'EOF'
#!/bin/bash
set -e
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
action="${1?}"
[[ "$action" == "config" ]] && {
config_type="${2?}"
arg="${3}"
[[ -z "$arg" ]] || {
key="${arg%=*}"
val="${arg#*=}"
}
if [[ "$config_type" == "ncp" ]]
then
config_path="/usr/local/etc/ncp.cfg"
elif [[ "$config_type" == "ncp-community" ]]
then
. /usr/local/etc/library.sh
[[ -z "${key}" ]] || {
set_app_param ncp-community.sh "${key}" "${val}"
get_app_params ncp-community.sh
exit 0
}
get_app_params ncp-community.sh
exit $?
else
echo "ERROR: Invalid config name '${config_type}'" >&2
exit 1
fi
[[ -z "${key}" ]] || {
cfg="$(jq ".${key} = \"${val}\"" <"$config_path")"
echo "$cfg" > "$config_path"
}
cat "$config_path"
exit 0
}
[[ "$action" == "file" ]] && {
file="${2?}"
if [[ "$file" == "ncp-version" ]]
then
cat /usr/local/etc/ncp-version
else
echo "ERROR: Invalid file '${file}'" >&2
exit 1
fi
exit 0
}
EOF
chmod 700 /home/www/ncp-app-bridge.sh
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /home/www/ncp-app-bridge.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
# NCP AUTO TRUSTED DOMAIN
mkdir -p /usr/lib/systemd/system

View File

@ -151,7 +151,8 @@ chmod 770 /var/www/ncp-web
# install NC app
rm -rf /var/www/ncp-app
cp -r ncp-app /var/www/
mkdir -p /var/www/ncp-app
cp -r ncp-app/{appinfo,css,img,js,lib,templates} /var/www/ncp-app/
# install ncp-previewgenerator
rm -rf /var/www/ncp-previewgenerator

View File

@ -5,7 +5,63 @@
cat > /usr/local/etc/instance.cfg <<EOF
{
"cohorteId": ${cohorte_id},
"canary": false
}
EOF
}
cat > /home/www/ncp-app-bridge.sh <<'EOF'
#!/bin/bash
set -e
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
action="${1?}"
[[ "$action" == "config" ]] && {
config_type="${2?}"
arg="${3}"
[[ -z "$arg" ]] || {
key="${arg%=*}"
val="${arg#*=}"
}
if [[ "$config_type" == "ncp" ]]
then
config_path="/usr/local/etc/ncp.cfg"
elif [[ "$config_type" == "ncp-community" ]]
then
. /usr/local/etc/library.sh
[[ -z "${key}" ]] || {
set_app_param ncp-community.sh "${key}" "${val}"
get_app_params ncp-community.sh
exit 0
}
get_app_params ncp-community.sh
exit $?
else
echo "ERROR: Invalid config name '${config_type}'" >&2
exit 1
fi
[[ -z "${key}" ]] || {
cfg="$(jq ".${key} = \"${val}\"" <"$config_path")"
echo "$cfg" > "$config_path"
}
cat "$config_path"
exit 0
}
[[ "$action" == "file" ]] && {
file="${2?}"
if [[ "$file" == "ncp-version" ]]
then
cat /usr/local/etc/ncp-version
else
echo "ERROR: Invalid file '${file}'" >&2
exit 1
fi
exit 0
}
EOF
chmod 700 /home/www/ncp-app-bridge.sh
sed -i 's|www-data ALL = NOPASSWD: .*|www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /home/www/ncp-app-bridge.sh, /sbin/halt, /sbin/reboot|' /etc/sudoers
ncc upgrade